לחשוב Developer eXperience (DX)

מדי כמה זמן יוצא לי "להמציא" מונח, שפשוט נשמע הגיוני – ואז לגלות שהוא בעצם נמצא כבר בשימוש. כך היה עם "יהלום הבדיקות" (כ contra לפירמידת הבדיקות) שפשוט כתבתי עליו בפוסט – ואז ראיתי אותו מופיע במקורות אחרים (ומכובדים). כנ"ל על DX (על משקל User eXperience, קרי UX) שהשתמשתי בו בפוסט – ופתאום נוכחתי שזה מונח שכבר בשימוש.

בפוסט – לא אציג שום טכניקה, פרקטיקה, או סגנון חדש לשיפור התוכנה שלנו, רק מטאפורה.
אז, בעצם, מה היא שווה? למה לדבר עליה?

DX היא מטאפורה חזקה – וזה הכל. אם אתם עוסקים בעולמות הניהול ו/או הארכיטקטורה – אתם בוודאי מודעים לעוצמה ולערך של מטאפורה מוצלחת. כשהמנהל של חברת דיסני רצה לשפר את חווית הבילוי בפארקים של החברה, אחד הדברים שהיו לנגד עיניו הוא איך לגרום לעובדים שעוטים תחפושות של דמויות של דיסני – לספק חוויה טובה יותר למבקרים.
הפתרון היה הכלל (להלן: מטאפורה) "כל הפארק הוא במה". כלומר: כל עובד שעוטה תחפושת הוא שחקן, ובכל רגע שהוא בשטח הציבורי של הפארק הוא חייב לנהוג כשחקן, ולשחק את הדמות עד הסוף. לא מכניסים יד לתחפושת כדי לגרד בראש, לא מעשנים סיגריה או מורידים את התחפושת בכדי לשתות.

זה עבד! ומאז המטאפורה הזו התפרסמה כדוגמה.

"DX" שימושי כמטאפורה כדי להזכיר לנו, כאנשי תוכנה, לפתח תוכנה טוב יותר בכמה היבטים. ידענו כבר קודם שקוד טוב הוא חשוב, כפי שהעובד של דיסני ידע כנראה שמיקי-מאוס מעשן זה לא לגמרי לעניין – אבל החידוד יכול לעזור לנו, כדי להישאר מפוקסים בכתיבת קוד טוב יותר.

באיזה היבט של "חווית מפתח" אנחנו מתמקדים? ממה מורכבת "חווית הפיתוח" של מתכנת?
כפי שאפשר לטעון שחווית המשתמש במיון של בית חולים לא מוגבל לאפליקציית הרישום בכניסה לבית-החולים, אלא כולל כיצד ניגש אליו הצוות, כיצד/האם מדברים מעל ראשו, וכמה רעש יש מסביב – כך DX יכול להתחיל מקוד, ולהמשיך לתיעוד, כלים, סביבת עבודה, ועוד.

בעולם בו מחסור במפתחים הוא מחסום עיקרי לצמיחה של ארגונים, DX הופך למושג שמעניין את הנהלות-הארגונים: הן בהיבט שימור/גיוס עובדים, והן בהיבט של פריון. למשל: אם כלים טובים יותר (הוצאה כספית) יגרמו למפתחים להיות מרוצים יותר ויעילים יותר – זו החלטה קלה להנהלה. אין ספק שארגוני תוכנה רוצים מפתחים מרוצים ויעילים.

סקר של מקינזי בקרב "מומחים"/מנהלים בכירים מה משפיע על הפריון של מפתחים

Code-level DX

אני רוצה להתחיל בשימוש במטאפורה של DX לרמת הקוד, בכדי לעזור לנו לכתוב קוד טוב יותר.
נתחיל בדוגמה קצת קשוחה… אובייקט ה java.util.Date (שהוחלף לחלוטין בג'אווה 8)

  println(new Date(2020, 5, 35)) // Result: Mon Jul 05 00:00:00 IDT 3920
  println(new Date(120, 4, 1)) // Result: Fri May 01 00:00:00 IDT 2020

כאשר אני מאתחל את אובייקט ה Date בג'אווה ל 35 במאי 2020, אני מקבל את התאריך 5 ביולי 3920.
למה?? איך?!
אובייקט ה Date הציג Interface שהוא פשוט זוועה, במיוחד בגרסאות הראשונות שלו (ה constructor בו השתמשתי הוא deprecated):

  • את השנה מצפים שאספק מ 1900 (על משקל Java Epoch 🤯), כך כ 2020 היא 120.
  • את החודש מצפים שאספק באינדקס-0, כך ש 5 הוא יוני (כי 0 הוא ינואר… לא ברור?)
  • אם יש גלישה של ימים (35 ביוני) אז לא תיזרק שגיאה, אלא יוסיפו את הימים כבר לחודש הבא (ההפך מ Fail Fast) – וכך הגענו ל 5 ביולי.

אפשר לטעון, "זה ה API של ה Class. הכל כתוב בתיעוד, קראו את התיעוד – ותלמדו למה לצפות ואיך לעבוד עם זה."
אני רואה את הטיעון הזה עולה מפעם לפעם, בדוגמאות פחות קיצוניות אמנם – אך אותו טיעון, וזה טיעון שגוי בעיני.

אם אתם רוצים לעשות DX טוב אתם צריכים לכתוב API/Interface שמפתחים ייהנו לעבוד אתו. לא… לא "יצליחו לעבוד" אתו – ייהנו לעבוד אתו!
השאיפה הזו להנאה עשויה להציב את הרף הנכון לרמת הקוד שאנו מבקשים, כפי ש"כל הפארק הוא במה" עזר לעובדים של דיסני להבין את רף החוויה שהם מצופים היו לתת למבקרים בפארקים של דיסני.

הנה דוגמה הרבה פחות קיצונית:

fun DataPoints.plus(other: DataPoints): DataPoints {
    val intersectKeys = this.keys.intersect(other.keys)
    val intersectEntries = (this.entries + other.entries).filterKeys { it in intersectKeys }
    return DataPoints(intersectEntries)
}

אני משתמש בפונקציה בשם plus, שבעצם מבצעת חיתוך של איברים – כלומר: אני מסיים עם פחות איברים.
יכול להיות שבהקשר של Data Points זה הגיוני (מי יודע?! – נניח שאני לא מכיר את הדומיין), אבל זה לא אינטואיטיבי לי כאדם ולשכל הישר שלי. האם אני כותב את הקוד כ Domain Expert או כאדם?

כנראה שאני קופץ בין שניהם, ואם לרגע אני מסיר את "כובע" ה Domain Expert וחושב בהיגיון פשוט של אדם – זה מבלבל. אם בזבזתי עכשיו חצי שעה לדבג קוד רק כדי להבין שהפונקציה plus מצמצת את מספר האיברים – זה כנראה יוביל לכך שלא אהנה מה interface של הפונקציה – וזה אומר נקודת כישלון של DX. המחשבה על DX אמורה לשים את כל הטיעונים של "הצודקים לכאורה" של ה Domain בצד "אבל ברור ש…", "אבל ידוע ש…", אם אני מתבלבל כי לרגע חשבתי כאדם ונותרתי עם חוויה לא טובה – זה לא DX טוב.

הפתרון: לקרוא לפונקציה combine שתתאים גם לתאר את הדומיין המורכב, אבל גם לא "תכשיל" חשיבה אנושית בסיסית שלי. שם כללי כמו combine יחייב אותי לבדוק, ולא להניח – מה הפונקציה עושה. בדיוק כמו שאנשי UX מכים על חטא אם כיתוב על כפתור גורם לאנשים להתבלבל ולבצע את הפעולה הלא-הנכונה, והם לא יאמרו לכם (אני מקווה) "המשתמש היה צריך לחשוב טוב יותר", או "הוא לא מבין מספיק, מה הוא חשב שיקרה בשלב הבא?"

דוגמה אחרונה, הסתכלו על ה interface הבא:

interface NoFun {

  fun updateCustomerDataPoints(customerId: CustomerId, dataPoints: List<DataPoint>)
  
  data class DataPoint(
    val type: DataPointType, 
    val value: DataPointValue, 
    val effectiveDate: LocalDate
  )

}

במקרה הזה אנו מעדכנים את הלקוח עם DataPoints מסוימים, אבל בתסריטים מסוימים חשוב לעדכן את ה effective Date על כל ה Data Points. ברור למומחה העסקי מדוע זה נכון, אבל אני מקווה שברור לכם הקוראים – מדוע לצרכן של ה API זה לא אינטואיטיבי, וקל לשכוח את זה – שלא לומר שלעבור על רשימה של איברים שאני שולח ולשנות אותם עלול להרגיש כמו משהו לא נכון או hacky שאני כמשתמש עושה. פעולה כזו לא מתאימה ל"שימושיות גבוהה"

שינוי קטן ב API (הוספה של שדה אופציונלי של effectiveDate) יכול לשדרג את השימושיות שלו בצורה משמעותית:

fun updateCustomerDataPoints(customerId: CustomerId, dataPoints: List<DataPoint>, effectiveDate: LocalDate? = null)

המימוש של ה API יעבור על ה Data Points ויעדכן את ה effective date שלהם – זו פעולה פשוטה. אבל עצם זה שהאופציה ניתנת לי בחתימת ה API: א. עוזרת לי לזכור אותה, ב. נוסכת בי בטחון שאני עושה את הדבר הנכון – ולא איזה hack שתוצאותיו לא ברורות. נראה שחסר כאן תיעוד שמסביר כיצד ומתי בדיוק לשלוח ערך ב effective date – זה עדיין לא טריוויאלי מספיק מתוך החתימה.

החשיבה שה API אמור לשרת ולהיות הכי טבעי / קל / נוח עבור צרכן ה API – היא לא רעיון חדש (כתבתי עליו בבלוג בעבר). אבל לפעמים המחשבה ש "כל מפתח צריך להבין מה קורה במערכת" יכולה לגרור אותנו לפספס וליצור APIs פחות טובים.

הטיעון ש "אנחנו רוצים לתת API שכיף ונוח לעבוד אתו" (בלי קשר לרמת המומחיות של הצרכן) עשוי להכריע את הכף לטובת השימושיות הגבוהה / יישום POLA. אולי לא כולם יסכימו שה API הראשון מבלבל / מכשיל את המשתמש, אבל כולם יסכימו בוודאי שהוא לא "מפנק" ואפשר לעשות אותו נוח ו"כיפי" יותר לשימוש. זה מספיק. UX גבוה הוא קונספט מוכר ומוסכם, ואף אחד לא יצפה ש Gmail יבקש ממני להגדיר את ה HTTP Headers של ההודעה – רק בגלל שאני מומחה ואני יכול, נכון?

מה מפתחים חושבים שמבזבז להם זמן – סקר של Pluralsight
על מי המפתחים סומכים שיודע מה שואב להם זמן – סקר של Plusralsight שקצת סותר את סקר המנהלים/מומחים שהצגתי בתחילת הפוסט.

DX ברמת ארגון הפיתוח

DX הוא מטאפורה מצוינת למפתחים, שיעזור להם ליצור APIs, Intefaces, ובכלל קוד טוב וקריא יותר. השאלה "האם מי שישתמש בקוד שלי עומד ליהנות?" – תוביל למסקנה שונה (והרבה פעמים: טובה יותר) מאשר השאלה "האם הקוד הזה בסדר / תקין / מסודר?"

DX היא גם מטאפורה חשובה מאוד ברמת ארגון הפיתוח, בתקופה בה חשוב לארגונים מאי פעם לשמר את העובדים הללו, להפיק מהם את המיטב, וליצור מותג חיובי של החברה – כחברה שטוב לעבוד בה.

תלונות כמו:

  • "ה Build אטי"
  • "ה Security Tools והדרישה להכניס סיסמה כל פעם – פשוט מטרידים"
  • "לפתוח באג בג'ירה זה פשוט סיוט… למה זה כ"כ מסובך?"

הן תלונות שעשויות לעבור מתחת לרדאר, תחת המחשבה ש"הם אנשים חכמים, הם יסתדרו" או ש"גם בימי היה ככה – זו המשמעות של לפתח תוכנה בארגון".
הלחצים על ארגוני הפיתוח – הם תמיד אדירים: יש המון מה לעשות, המון מה להספיק, ותמיד אנחנו רוצים טוב יותר ומהר יותר. פעם שמעתי כלל שלארגון פיתוח יהיו תמיד דרישות לעשייה גדולה פי 5 – ממה שהוא מסוגל לספק. ככל שהוא יגדל – כך יגדלו הדרישות.
את חווית המפתחים – אפשר לשכוח, ולהזניח, ובתקופה שלנו, זה אולי פחות טוב אפילו מבעבר.

השאלה למנהלים היא "האם כיף לפתח בארגון / מערכת שלנו – יותר מחברות מקבילות?" היא שאלה רלוונטית. פיתוח, במיוחד של מערכות מורכבות, תמיד כולל אלמנטים שוחקים ומתסכלים. תמיד יהיה את הבאג המתיש ברמת התשתית שנחקור לאורך ימים או שבועות, ונחווה שוב ושוב אכזבות שכמעט פתרנו אותו – אבל עדיין לא.

זול יותר וקל יותר לספק לעובדים עוד יום כיף, עוד הטבות, עוד מתנה מגניבה. זה רק עניין של כסף.

שיפור חווית הפיתוח דורש מאמץ אמיתי, חשיבה, ומיומנות גבוהה – ולא כל מאמץ יסתיים בהכרח בהצלחה. זה מסע. תמיד גם יהיו תלונות, ומפתחים לא מרוצים, על כל מצב שלא נגיע אליו – מה שמרפה את ידי המנהלים להשקיע בנושא.

מצד שני, התמורה בחוויית פיתוח טובה יותר עשויה להיות מכפיל כוח אמיתי לארגון:

  • פריון גבוה יותר: מפתחים כותבים יותר פיצ'רים / משפרים את המערכת, במקום לבזבז זמן במקומות אחרים.
  • שביעות רצון: הגיוני שמפתחים יעריכו את השיפור שנעשים בסביבת העבודה שלהם, במיוחד אם מראים את זה, ומשתפים את המפתחים בזה.

קראתי כמה מאמרים שטענו ש DX גבוה יותר (איך מודדים?) משפיע בצורה משמעותית על הצלחת הארגון ("פי 4-5") ועל שביעות הרצון של העובדים – אבל לא מצאתי נתונים משמעותיים ששווה להציג. רק דיבורים.

אני מניח שאלו הדברים שבשלב הזה הם עניין של אמונה: כמה אתם מאמינים ששיפור חווית הפיתוח של המפתחים בארגון שלכם תשתלם בפריון ושביעות רצון גם בלי יכולת למדוד כל שיפור? או עד כמה אתם נצמדים למה שניתן למדוד ותשקיעו רק היכן שיש ROI מוכח (כנראה: בעיקר בפריון).

העיקרון הראשון בשיפור ה DX למפתחים ברמת הארגון הוא להקצות בצורה בלתי-מפשרת משאבים איכותיים ומשמעותיים לנושא. אלו לא נושאים קלים.

העיקרון השני הוא לבדוק ולתעדף היכן להשקיע. גם אם אנחנו מאמינים שזו ככלל השקעה טובה – ברור שניתן להשקיע המון מאמץ בשיפורים שלא יהיו משמעותיים.

מונח מתאים מעולם ה UX הוא ה Friction Log – תיעוד אזורים בהם קשה למשתמשים להתקדם ב UI המערכת והם חווים בהם תסכול. באופן דומה כנראה שכדאי לבקש מהמפתחים לשתף ולתעד – היכן הם חווים קושי, עיכובים, ותסכול ברמת הפיתוח, ובטוח שיש הרבה כאלו.

חשוב להקשיב ולהראות עניין בכאבם של המפתחים, קודם כל מתוך ניהול בסיסי. דבר שני – דרך ה Friction Log אנו יכולים לגלות ולתעדף טוב יותר, נושאים שנכון לשפר אותם.

כמו בארכיטקטורה של מערכת, כדי להיות יעילים הרבה פעמים שווה להשקיע בלהפוך בעיות ברמת חומרה "High" לבעיות ברמת חומרה "low" או אפילו "רק medium" ולעבור לבעיה הבאה – מאשר לנסות להעלים את הבעיה לגמרי. Build Pipeline איטי מציק אם הוא אורך 30 דקות, אבל כנראה ש 10 דקות זה בסדר. המאמץ להביא אותו ל 2 דקות – עשוי להיות אדיר, ולא משתלם.

נתקלתי בפונקציות לא טכניות של Developer Enablement – מישהו בארגון שמזמין קורסים, קונה כלים, מארגן כנסים וסקרים בקרב המפתחים בכדי לשפר את החוויה וההווי שלהם. קשה לי להאמין שזה מודל יעיל ש"יפגע" במקומות הקשים והכואבים. כנ"ל לגבי מישהו מצוות ה Operations (או "DevOps") שאחראי על העניין. בטוח שיש להם מה לתרום, אבל עדיין לא ראיתי את האדם שאינו מפתח – ומצליח להבין עד הסוף את הכאבים של המפתחים, ולהביא פתרונות עומק למה שבאמת כואב.

אני מאמין ש DX גבוה יכול להגיע רק מתוך שיתוף פעולה פרואקטיבי עם המפתחים וגם מניהול של העניין ע"י מישהו שמגיע מעולמות הפיתוח. לא Operations ולא תפעול.

סיכום

בסך הכל מה שיש לי להציע בפוסט הזה היא מטאפורה חדשה – לבעיות ורעיונות קיימים.

האם המטאפורה העדיפה (בשאיפה) – תוביל לתוצאות טובות יותר? אתם תגידו – אבל יש לי אופטימיות שייתכן וכן.

שיהיה בהצלחה!