אפשור מול ריסון: קרב האור באופל של ארכיטקטורת התוכנה?

פעמים רבות בחיי השתתפתי בשיחה שהחלה בערך כך:

“אז למה שלא נשתמש ב GraphQL / בסיס נתונים משותף / נעבור מ p2p ל broadcast / נסיר שכבת הפשטה מהתוכנה? – זה יעזור לנו לכתוב תוכנה מהר יותר!”.

“אבל מה עם המחירים?”

“איזה מחירים? מה יותר טוב מלכתוב קוד מהר יותר? למה לא?”

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

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

הוויכוח הזה לא התחיל היום. הוא לא ויכוח בין אדם אחד לאחר, ולא ויכוח ייחודי למיקום יחיד.

זה ויכוח שמתרחש בעולם התוכנה יום וליל, מסביב לגלובס.

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

במבט-על הוא נראה כמו ה Game of Life בו “שטחים” נכבשים ומשתחררים:

מבוזר (קורה בכל פינה), גלובאלי (קורה בכל מקום), ומחזורי (כמה שנים האופנה לכיוון אחד – ואז מתאזנת לכיוון השני).

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

לופ שלא נגמר.

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

הנה כמה התכתשויות כאלו, שהתרחשו בקנה-מידה עולמי:

— ביבשת “מבנה התוכנה” —

Design Patterns נגד … מה שהיה קודם

YAGNI מול Design Patterns

S.O.L.I.D מול YAGNI

— ביבשת ה”תקשורת” —

SOA מול RPC

REST מול SOA

newer RPC (Thrift/gRPC) מול REST

GraphQL מול newer RPC

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

אז מה קורה שם?

הדיון בין אִפשור לריסון בעולם התוכנה דומה מאוד לדיון הכלכלי בנוגע למעורבות המדינה / שלטון מרכזי: מה עדיף? יותר רגולציה והגנה או שוק חופשי לחלוטין, חסר-חסמים?

כשעלויות המחייה עולות וכואבות – דורשים הסרת חסמים / פיקוחי.

כשמתגלה ניצול / חוסר צדק של חזקים מול חלשים – דורשים בקרה ורגולציה.

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

האם זו בעיה כ”כ סבוכה? NP-Complete? אי אפשר כבר להציב מחשב שיפתור אותה?!

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

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

ה Tradeoff בעולם התוכנה, הוא דיי דומה:

  • בצד אחד, אפשור (Enablement) – החלטות שעוזרות לנו לכתוב קוד בצורה קלה/מהירה יותר. הסרת חסמים.
  • בצד שני, ריסון (Restraint) – בניית מנגנונים שיגנו עלינו בפני טעויות, ישמרו את הקוד קל לשינוי.
למשל: אם משתנה הוא גלובאלי (נגיש מכל מקום בתוכנה [א]) – קל לגשת אליו ולהשתמש בו. למה להתאמץ?
אפשור גבוה גורר שימוש גבוה. אנו מרבים להשתמש במה שקל להשתמש בו – וכך יש סבירות גבוהה ששימוש במשתנים גלובאליים, שמקצרים כתיבת קוד, יהפוך במהרה לפופולארי ובעל שימושים רבים. מפתחים מעדיפים לכתוב פחות קוד – וטוב שכך!
אפשור כמעט תמיד עוזר בטווח הקצר (אחרת: מה הטעם?), ומחיריו לרוב מתבררים רק בטווח הבינוני או הארוך. שימוש במשתנים גלובאליים יכול להוביל למחירים הבאים:
  • שינוי בהגדרת המשתנה, למשל: טיפוס או פירוק שלו לשני משתנים שונים – עשוי להיות קשה מאוד. עשרות או מאות מקומת בקוד צריכים להשתנות. כל המקומות הללו – צריכים להיבדק ויש סיכון לרגרסיה. מכאן: עבודה שחשבנו שתעשה בשעה – עכשיו אורכת ימים, באופן בלתי צפוי.
  • למשתנים הגלובליים אין “הקשר” ולפעמים קשה לזכור / להבין מה משמעותם המדויקת. כל פרשנות שגויה עשויה להיות באג בתוכנה.
  • “פיצוץ” המרחב הגלובאלי במשתנים מקשה עלינו לבחור בין משתנים בשמות דומים, ולזהות מי הוא מי.
  • קשה יותר לכתוב בדיקות יחידה – לקוד שיש לו קשר למשתנה הגלובאלי.

אז מצד אחד אפשרנו – זזנו מהר, מהרגע הראשון.

מצד שני – בטווח הבינוני או הארוך – אנחנו משלמים מחיר שמאט אותנו.

מה עדיף?

הפתרון

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

לרוע המזל, אין לנו יכולת לבצע חישובים כאלו: לא ברמת הרזולוציה של העבודה x ריבוי המקרים, ולא ברמת חיזוי העתיד: כיצד הקוד יתפתח לאורך זמן.

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

הפעולה ההפוכה הייתה ריסון. ריסון מושג ע”י משמעת (“אסור להשתמש במשתנים גלובלאליים”) או ע”י מניעה (שפות חדשות הסירו את היכולת להגדיר משתנים גלובאליים).

שפת ג’אווה (1995), למשל, הלכה צעד הלאה וקבעה נורמה של שַׁלְפנים (Getter/Setters) על האובייקטים: הקצנה של הלקח של “אסור לאפשר משתנים גלובאליים” – על scope מצומצם בהרבה, הרי הוא האובייקט. אינספור מפתחים בזבזו במצטבר שנות חיים בכתיבה של getter/setter גם למשתנים שלעולם הגישה אליהם לא תשתנה, ולעולם לא יעשה בהם שימוש לרעה.

אחרי כמות אדירה של סבל (או לפחות: בזבוז) שהצטברו – המגמה התהפכה. שפות רבות מאפשרות ומעודדות גם חשיפה של משתנים שלא דרך שׁלְפנים, ודרכים אלנגנטיות להוסיף שלפנים רק בעת הצורך. Records שהוצגו בג’אווה 14 – באיחור רב, הם דרך אחת לחסוך כתיבה של שלפנים עבור מבני-נתונים (במובן ה OO שלהם).

הנה לנו עוד מחזור.

לכאורה נראה שחשוב ללמד אנשים לנתח את ה tradeoff בין אפשור וריסון – כדי שיחליטו טוב יותר.

אבל:

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

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

חשוב לציין שהצד השני, ריסון-יתר גם הוא קיים ומזיק לא-פחות. הנזקים מריסון-יתר, עשויים להיות גם הם אדירים: חשבו על EJB. חשבו על SOA.

בכל זאת, הסיכון הוא לא לגמרי סימטרי:

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

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

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

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

תסביר שוב: מה הנזק מאפשור-יתר / ריסון-יתר? אז מה עושים?

אפשור הוא הרצון להתקדם מהר ע”י הורדת חסמים. דוגמה קלאסית (ואמיתית): חשיפת נתונים מבסיס-הנתונים ישירות ב HTTP. למשל: אפליקציית הווב פונה ישירות לבסיס הנתונים ושולפת מידע (מסוים), וכך לא צריך לעבור דרך API GW ודרך ה Backend – חסכנו עבודת פיתוח ושיפרנו את הביצועים.

נתקלתי בתצורה הזו פעמיים בחיים. פעם באמת ישירות בצד-הלקוח, פעם אחרת דרך איזו שכבה רזה באמצע, ולאחרונה שמעתי רעיון דומה עולה שוב.

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

האם זה רעיון טוב או לא? זה תלוי בהקשר.

יהיו פעמים שכן – שזו תהיה הדרך הפשוטה והטובה לעשות את הדברים.

במערכת אפליקטיבית בה:

  • המודל, ומכאן סכמת בסיס הנתונים משתנה מדי-פעם.
  • שכבות אפליקציה מטפלות בנתונים (בכתיבה, אבל גם בקריאה), ומפעילות לוגיקה-עסקית עליהם – שגם היא משתנה.
  • דרישות צד-הלקוח משתנות לאורך זמן.
גישה ישירה מצד-הלקוח לבסיס-הנתונים עשויה להתגלות ככדור (תותח) ברגל. הפיתוח יתקדם מהר, אבל אז:
  • יתגלה באג שלא ברור מדוע הוא מתרחש: מישהו הוסיף קוד אפליקטיבי שאינו חל על הקריאה הישירה מבסיס הנתונים.
  • שינוי פשוט לכאורה, נניח: שינוי במודל שדורש שינוי בסכמה של בסיס הנתונים – הופך לשינוי קשה, ארוך, ובעל הזדמנויות רבות לתקלות.
    • יש כאן גם חוסר-הוגנות ארגוני פוטנציאלי: מפתח אחד אפשר וחסך זמן בפיתוח הפיצ’ר שהוא אחראי אליו, בעוד מפתח אחר נאלץ לשלם את המחיר היקר שנגרר מאותו אפשור.
    • נתקלתי במצבים כאלו פעמים רבות – והם לא מסייעים לתחושת השותפות והידידות בארגון, במיוחד אם צוות אחד מאפשר תדיר על חשבון אותו צוות אחר שמשלם את החובות-הטכניים של אותו האפשור.
ריסון הוא הרצון להגן על המערכת ע”י הצבת חסמים מלאכותיים. במילה “מלאכותיים” הכוונה היא שיש דרך לבצע את הפעולה באופן קל יותר – אך אנחנו יוצרים חוקים / מגבלות פיסיות שלא יאפשרו זאת.
דוגמאות:
  • השימוש ב private על members בפרדיגמת ה OO – הוא ריסון ברמת המיקרו. למעבד אין בעיה לגשת לכתובת הזו בזיכרון – אבל אנחנו מורים לו לא לעשות זאת (ומשלמים תקורה בביצועי התוכנה / זמן כתיבת הקוד).
  • כלים לניהול תלויות בין מודולים / מיקרו-שירותים או נהלים להגבלת ספריות ה open source שנכנסות למערכת: אין למתכנת בעיה להוריד כל ספריה ולהתשמש בה – אבל הארגון רוצה לבדוק את הספרייה מבחינת אמינות / תמיכה / כפילות / רשיונות / היבטי אבטחה – לפני שזה נעשה.
  • קונבנציות של קוד – הן ריסון. הקומפיילר יקבל סגנונות שונים ומשונים – אבל הארגון מחליט שבנקודות מסוימות הוא מקבל סגנון רק מסוים.
  • לפעמים גם בחירה של שפת-תכנות היא ריסון. שפת Go מחייבת מבנה אחיד ופשוט הרבה יותר משפה משופעת באפשרויות כמו רובי. מעבר מרובי ל Go – הוא ריסון ניכר.
דוגמה קלאסית לריסון-יתר הוא Closed Layered Architecture מרובת שכבות. בכדי להתגונן בפני בעיות שונות בכתיבת מערכת, הומצא מודל השכבות בו כל שכבה יכולה לקרוא רק לשכבות מתחתיה. בגרסה ה”סגורה” של המודל, כל שכבה יכולה לקרוא רק לקוד באותה השכבה או בקוד בשכבה אחת מתחתיה:
כלומר: אם קוד בשכבה 4 רוצה לקבל נתונים משכבה 2 עליו:
  • לקרוא לשכבה 3 ולבקש את המידע.
  • שכבה 3 תקרא לשכבה 2 ותבקש את המידע.
  • המידע חוזר במבנה הנתונים ששכבה 2 מכירה (אך שכבה 3 יכולה להשתמש בה).
  • שכבה 3 לא יכולה להעביר את מבנה הנתונים של שכבה 2 לשכבה 4 (אסור!) ולכן היא מגדירה מבנה נתונים משלה למידע שאותו היא חושפת בפני שכבה 4, לאחר המרה, כמובן.
ייעצתי פעם לארגון שעבד עם מודל של 6 שכבות סגורות. המערכת לא הייתה עשירה כ”כ ב business logic ורובה ביצעה אינטגרציה בין מערכות.
מידע רב עבר בין שכבה 2 לשכבה 6 – ורוב הקוד בשכבות 3, 4, ו 5 פשוט תרגום שוב ושוב קריאות, הלוך ושוב, למבנים שקולים – אך שונים. חשיפת נתונים בין שכבה 2 לשכבה 6 דרשה כמות נכבדת של קוד? על מה?!
אני בטוח שמישהו הגיע עם כוונה טובה. הוא קרא מאמר או שמע הרצאה על מערכת שעבדה ללא סדר / שכבות – והפכה לסיוט לתחזוקה. אני מדמיין שהוא החליט לא ליפול לפח ולעשות את “הטוב ביותר”. לא רק 3 שכבות – אלא 6! לא רק שכבות – אלא שכבות סגורות. Crème de la Crème!
הנזק כאן כמובן היה אדיר,שעות מפתחים ושחיקה רבה ממשימות בסיסיות. אני מקווה שהחברה בסוף יישמה את הרעיון הפשוט של לעבור למודל פתוח או לפחות פתוח-למחצה.
נ.ב. האוונגליסטית לשינוי הייתה דווקא מישהי שרק התמנתה לתפקיד ארכיטקטית. לארכיטקטים הותיקים והמנוסים בחברה היה blind spot למצב, ולאבסורד שבו.

סיכום

אז מה אפשר לקחת מכל הסיפור הזה?
דבר ראשון – אני מקווה שחידוד הנושא יעזור בפעמים הבאות שתתקלו בדילמה שבין אפשור וריסון – ותצליחו להסתכל על הדילמה במבט מעט יותר מפוכח.
דבר שני – הזהרו מהטיעון (נא לקרוא במבטא צרפתי): “ואז: Walla!, נפתח קוד הרבה יותר מהר!” וגם מהטיעון ההופכי “ואז, !Walla, לא עוד בעיה מסוג X במערכת – וחדל לבזבוז הזמן”.
כשמישהו מציע אפשור / ריסון משמעותי במערכת, אני אישית, הייתי רוצה לראות אותו\אותה בפנים מיוסרים (מלבטים). בדרך אני נתקל בהם כשפניהם זורחות והם מלאי אנרגיה (שלא לומר: זחיחות) – אני ארגיש הרבה יותר טוב כשהם יהיו מיוסרים, ויאמרו משהו כזה:
“אני חושב לאפשר/לרסן את הדברים כך וכך, הנה ההזדמנויות (מציאותיות), אבל הנה גם הסיכונים (שמטרידים גם אותי). מה אתם אומרים?”
זו הדרך המקצועית לגשת לדיונים כאלו. כשכולים מכירים בכך שמדובר ב tradeoffs לא פשוטים – הדיון הופך לקל ויעיל יותר.
אני משוכנע שאין פתרון אחד כולל / נכון-תמיד ל treadeoff שבין אפשור לבין ריסון.
אבל אני גם מאמין שבחשיבה נכונה, אפשר ברמת המערכת / הארגון הספציפי – למצוא tradeoffs הרבה יותר טובים מבחירה שרירותית. ואם טועים – אפשר גם לתקן.
הפסיקו להסתכל מתחת לפנס, ולהניח שהדברים עומדים להסתדר ע”פ התסריט האופטימי שבו הייתם רוצים!
לא עוד “הפיתוח יטוס מעכשיו בטיל”.
או “מערכת שהיא בטון יצוק”.
דיי. מאסתי בתיאורים כאלו.
בואו נהיה מקצועיים.
שיהיה בהצלחה!

[א] בשפות שלא תומכת משתנים גלובאליים ניתן לחשוב על משתנה שעל אובייקט שנגיש לכולם.

מבוא ל Domain Driven Design

Domain Driven Design, או בקיצור DDD היא מתודולוגיה שהוצגה בשנת 2003, בספר בשם דומה.

מתודולוגיית ה DDD השפיעה על רבים בתעשייה, והיא זוכה בשנים האחרונות לפריחה מחודשת.

מחבר הספר, אריק אוונס (Eric Evans), לא היה דמות מוכרת ו/או מקושרת בעולם התוכנה.
הוא לא עבד בחברה גדולה או בעלת פרופיל תקשורתי מוכר.

בעצם – הוא היה דיי אלמוני.

עד היום, אין ערך בוויקיפדיה על שמו. פרופיל הלינק-אין שלו לא מזכיר דבר לפני 2003, ואפילו גוגל, אבוי לבושה, טועה – ומקשר את הספר שכתב לפרופסור כלשהו להיסטוריה – בשם דומה. בכדי למצוא פרטים על אריק הנכון פרטים – צריך להתאמץ!

פאדיחה!

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

הפרט ההיסטורי שיכול להשלים חלק מהתמונה הוא הפוסט הבא, דף הפרופיל של אריק ב WikiWikiWeb, הוויקי הראשון בעולם – שהוקדש לרעיונות בהנדסת תוכנה:

את WikiWikiWeb תחזקו Kent Beck ו Ward Cunningham, וברגע שדמות מוכרת כמו Kent Beck התלהבה מהספר – הדלתות נפתחו, וכמעט כל עולם התוכנה שמע על הספר.

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

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

— fast forward —

בערך ב 2014, כשאנשים כמו Martin Fowler, ו James Lewis ניסו להסביר לעולם את רעיונות הארכיטקטורה של MSA – הם השתמשו במונחים כמו Bounded Context וחזרו להזכיר את DDD. רעיון משפיע אחר הוא הרעיון של Event Sourcing (המופיע בספר פשוט כ “Domain Events”) – שיש לו קשר חזק לבאזז אחר: CQRS.

אריק חזר להופיע בכנסים חשובים, ואפילו יש כמה כנסים חדשים שמוקדשים ל DDD (למשל: DDD Europe). במשך השנים יצאו כמה ספרים נוספים על DDD (אריק הוציא תקציר “reference” לספר המקורי) וניתן היום למצוא מקורות כמו DDD Weekly – שהחל את דרכו רק בשנה שעברה. כמות הכתבות ב Medium והדיונים ב Hacker News לגבי DDD (ע”פ תחושה אישית ולא מדויקת שלי) – והלכים וגדלים.

DDD חזר למרכז הבמה!

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

הנה מתחילים!

אז הנה זה לפניכם, רשימת ה Patterns שמציג הספר והקשרים ביניהם:

מה… אתם צריכים דקה בכדי לעכל את התרשים? … שעה?…….. יותר?!

לא לא.. – חכו. זה לא ילך ככה.
בואו נתחיל מהתחלה, ולא נסתבך. יותר מדי אנשים הסתבכו כבר עם DDD.

DDD מציג רעיונות רבים, אבל מה העיקר?
מהם 3-4 הרעיונות הכי חשובים שהבנה שלהם יכולה לתרום לשיפור מעשי במאמץ הקטן ביותר? (זו תמיד שאלה טובה…)

רואים בתרשים למעלה את ארבעת המעגלים הגדולים ביותר? – אלו הם גם הרעיונות החשובים ביותר במתודולוגית ה DDD.
אמנם שלושה מהם מכוסים בספר בשלב מאוחר (פרקים 14 ו 15 !!) – אולם אריק בעצמו מספר שזו הייתה טעות, שהוא לא נתן לרעיונות הללו את הדגש הנכון. שאם היה כותב את הספר מחדש, אלו ככל הנראה היו פרקים 2 ו 3.

בגדול אפשר לומר שמתודולוגית ה DDD עוסקת בשני אזורים:

  • בניית מודל קונספטואלי – שמתאר בצורה “טובה” את המוצר, ואת הביזנס, בהקשר של תוכנה.
  • מיפוי המודל הקונספטואלי ל Design (כלומר: מבנה של מערכת תוכנה)אזור זה מכיל שורה של רעיונות כיצד למפות תיאור של מודל עסקי לתוכנה. אזור זה מתחלק גם הוא לשני שלבים:
    • “תכנון אסטרטגי”
    • “תכנון טקטי”
האזור הראשון, המודל הקונספטואלי – הוא המהפכני יותר לאנשי תוכנה. אם תוכלו לקחת מ DDD רק דבר אחד – הוא החלק היותר משמעותי.
הרעיון של מודל קונספטואלי לכתיבת תוכנה (עסקית) הוא לא חדש בכלל, אבל DDD חידד ושיפר כמה רעיונות.האזור השני של DDD הוא מורכב, מפורט, והאמת: גם לא כ”כ עדכני (כי טכנולוגיות השתנו, וסגנון ה Microservices פותר חלק מהדילמות). למען האמת, טכניקות ה design של DDD הן מעניינות – אך לא הוכחו כמוצלחות במיוחד בפועל. יש שם רעיונות יפים, ושכדאי להכיר – אבל יותר כרעיונות בודדים ולא כ Framework סדור.

הספר מציג Framework למימוש המודל הקונספטואלי כתוכנה, ומשתמש בעקרונות Object-Oriented בשילוב של רעיונות שאינם Object-Oriented אך הוכיחו את עצמם לאורך שנים. מבנים כמו Aggregate ו/או Repository – הם לא “Object Oriented טהור”. שווה אולי לציין שספרי מידול קודמים נהגו לחתור למודל טהור של “אובייקטים בזיכרון” ולא התייחסו כ”כ לבעיות הארציות כמו – שמירה לבסיס נתונים רלציוני. זה מלכלך את המודל.
למשל:  1.0 EJB היה אמור להיות Framework שמאפשר למתכנת-הממדל לחשוב על אובייקטים ולא לדאוג לבעיות כמו אכסון ושמירה. זה עבד טוב כל עוד היו פחות מ 10 משתמשים במערכת 😉

רעיון כמו Event Sourcing (אותה סקרתי בפוסט קודם) – הוא לא ממש Object-Oriented – אך הוא רעיון מוצלח ושימושי.

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

עוד כמה מילים על “המודל”

המודל הוא תיאור של מציאות (עסקית) מסוימת.
מקובל לתאר אותו ב UML – אבל לא חייבים. מה שחשוב שהמודל יהיה ברור, ושלא משתמע לשני פנים.

אני מעדיף את המונח (המקובל) “מודל קונספטואלי” בכדי לתאר את המודל שהוא רעיוני ולא ממש קוד.
במודל קונספטואלי הישויות הם רעיונות: “חשבונית”, “לקוח”, “ספק” – ולא משנה כ”כ כיצד ממשים אותם. בתוך המודל אפשר לשכוח לרגע מ Scalability, בסיסי נתונים, או בעיות של רשתות תקשורת.

בהמשך הפוסט אשתמש במונח “מודל” – כאשר הכוונה היא למודל קונספטואלי.

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

מודל ראשון:

מקור: https://martinfowler.com/apsupp/accountability.pdf

או אולי המודל השני?

מקור: https://martinfowler.com/apsupp/accountability.pdf

כמובן שכמו Design של תוכנה, אין “מודל טוב” או “מודל רע”. יש מודל מתאים יותר ומודל מתאים פחות לצורך הקונקרטי.

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

כלל מנחה: “!Make the model as simple as possible – but no simpler”

אני אוהב את הדוגמה של אריק על המפה (העתיקה) של חבל ארץ בסין:

מבחינה גאוגרפית המפה היא מעוותת, חסרה פרטים רבים, ומלאת שגיאות!

מבחינת שבט שגר בחבל הארץ הזה היא מאירת עיניים ושימושית למדי – היא מציינת באיזה כיוון מתגורר כל שבט. היא מדגישה את מה שחשוב למשתמשים שלה! מתארת את “הביזנס”.

מפה מודרנית, “מלאה”, ו”מדויקת” – היא כמעט חסרת-תועלת לאותו משתמש.

על אומנות ה Modeling

שימו לב שהרעיונות של DDD לא מתיישבים בקלות עם כמה מתודולוגיות נפוצות – למשל SCRUM.

ב SCRUM, יש תפקיד בשם Product Owner שהוא המתורגמן בין אנשי הביזנס / הלקוחות – למתכנתים. כשיש PO אין צורך לדבר עם לקוחות: ה PO מדבר עם כל הלקוחות, מרכז את התובנות שלהם לתמונה אחידה וברורה – שקל למפתחים לצרוך.

DDD מציגה רעיון שנקרא Hands-On Modeler – המתכנת (להלן “Hands-On”) צריך להיות גם ה Modeler – האדם שמגדיר את המודל, מבין אותו, ומכאן מבין גם את הביזנס ונמצא בקשר עם לקוחות המערכת. זה לא צריך להיות כל מפתח (Every) אלא ייצוג הולם של אנשים מצוות הפיתוח (Any).

שנייה אחת. יש כבר רעיון שכל מפתח צריך להיות איש ה QA של עצמו (TDD?), או הרעיון שכל מפתח צריך להיות איש ה Operations של עצמו (Continuous Deployment?).

האם באמת מפתח יכול להיות גם QA, גם איש Operations, וגם איש Product – ועוד להספיק לכתוב קוד משמעותי?!
כנראה שלא ממש. הסיבה שכל אחת המתודולוגיות הללו “מלבישה” למתכנת “כובע” נוסף חדש – היא כי ממשקי עבודה (“Seams”) הם מקור לתקשורת נחותה יותר (“טלפון שבור”) ואופטימיזציה לוקאלית בכל צד – מתכון מוכך לבעיות.

  • ארגונים שאיכות היא עניין מאתגר ומורכב במיוחד עבורם – כנראה ידרשו מהמפתחים גם לעשות QA.
  • ארגונים שסביבת הפרודקשיין שלהם מאתגרת – כנראה יידרשו מהמפתחים לעשות גם Operations.
  • ארגונים שהתאמת המוצר ללקוח היא חלק מאתגר במיוחד בעיסוק שלהם – יאמצו DDD ו/או Lean Startup.
    • נחדד: אם הלקוח הוא consumer אחד מני רבים – כנראה שגישת ה Lean Startup ושורה של ניסויים – היא עדיפה. אז נרצה שמפתחים יציעו ויבצעו ניסויים.
    • אם הלקוח הוא Domain Expert שיש מעטים כאלו שאפשר לגשת ולתחקר אותם – זה ה Sweet Spot של DDD.

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

נחזור ל DDD, ופיתוח תוכנה עסקית, שהלקוחות שלה הם עובדי-ידע בעלי מומחיות בתחום כזה או אחר.

ללקוחות יש תפיסה מסוימת של העולם שהם הסכימו בינם לבין עצמם עליה.
בד”כ יש “תפיסה מקובלת בתחום”, עם “התאמות מקומיות” (שחשובות לא פחות, אולי יותר).

אם זו הביזנס הוא תחנת מוניות מוניות אז ה Domain Experts חושבים על “היצע” (נהגים) ו”ביקוש” (נוסעים) ועל אחוז ההיענות של נהגים לנסיעות ו/או אחוז ההזמנות שסופקו. הדרישות החדשות שלהם לתוכנה שנכתבת – יגיעו בעולם המושגים הזה.

כאשר המערכת (תוכנה) איננה ממופה 1:1 למודל הקונספטואלי של הלקוחות – יישום דרישות חדשות עשוי להיות מפרך, ומרובה טעויות:

“אנו רוצים שתשלום יהיה גם במזומן או ב Paypal”
– “אין מצב. אנחנו תומכים רק בכרטיסי אשראי. שינוי עכשיו יהיה מ-ט-ו-ר-ף!”

“אנו רוצים שהזמנה אחת תוכל להכיל שתי נסיעות: הלוך וחזור”
– “אנחנו נאלץ ליצור שתי הזמנות – אחת גלויה ואחת חבויה ולקשר אותן איכשהו. ולחייב שזה אותו נהג ואותו נוסע ועוד כמה דברים…”

יותר גרוע: ככל שבאות עוד ועוד דרישות – מודל התוכנה “יתעקם” ויתרחק מהמודל של ה Domain Experts. התהליך הזה נקרא ב DDD בשם Corruption.
“אנו רוצים שהזמנה שכוללת נסיעת חזור תוכל להיות עם נוסעים שונים”
– “הממ… באמת? זה ממש חשוב? נצטרך 8 שבועות בכדי לממש את זה. אולי אפשר לוותר?”

“למה מורכב? תצרו נסיעה עם נוסע אחר. נשים שדה ב UI להזין את זה”
– “אתם לא מבינים. זה ממש מסובך! צריך שה HiddenOrder עכשיו יהיה ExpandedHiddenOrder שנשמר בטבלה אחרת וגם מכיל נוסע שיכול להיות אחר, והטיפוס הזה מדלג על ה Validations כבר יש לנו במערכת עבור הסיפור ההוא של נסיעת חזרה פשוטה…. ו….. ואז….”

“??? ….  אנחנו לא ממש מבינים למה זה הפעם מורכב. תוכנה זה תחום בלתי-צפוי – מסתבר…”

בקיצור: הסתבכנו!

כאשר המערכת (תוכנה) ממופה בקירוב 1:1 למודל הקונספטואלי של הלקוחות – הדרישות החדשות הופכות להיות פשוטות יותר ליישום.

כדי לבנות תוכנה שממופה 1:1 למודל הקונספטואלי – אנחנו צריכים להבין את המודל (הנפלא!) הזה שנמצא במוחותיהם של ה Domain Experts. הבנה שלו תאפשר לנו לקבל דרישות חדשות בהרמוניה.

המודל, למרבה הצער, לא נמצא כקובץ במחשב של הלקוחות. הוא לא קיים כתרשים UML או אפילו לא כ XML – פורמט שאנחנו לא מחבבים.
הוא נמצא שם בראש, לא של אדם אחד – אלא מספר אנשים, ולא פשוט לשלוף אותו.
בעצם: סביר יותר שנוכל רק ליצור קירוב שלו, ולא נוכל לעולם להגיע ל”מודל מדויק”. עדיין, קירוב – הוא טוב בהרבה מכלום.
זהו תהליך ה Modeling.

אם המודל מוכר רק ל PO או לארכיטקט – Corruption הוא בלתי-נמנע.

המפתחים הרי משנים קוד כל הזמן, ואם הם לא מודעים למודל – רק טבעי שיסטו ממנו. כדי לשמור על מודל יציב ונכון, חשוב שחלק מהמפתחים יהיו בקיאים במודל. שהמודל יהיה חשוף לידי כל, ובעצם יהיה כלי עבודה משותף וחשוב כמו New Relic או Github.
כמו כן ה Modelers לא יכולים להיות מנותקים מקוד. הרי המודל בא לשרת תוכנה – ולא להיות מודל “כללי”. תהליך המידול דורש גם הבנה בתוכנה וגם בביזנס.
ברוכים הבאים ל DDD 🙂

העקרונות הראשיים של DDD (בקצרה)

המודל
דיברנו.
איך ממדלים בצורה טובה? – הייתי ממליץ להתחיל עם הספר Analysis Patterns.
Ubiquitous Language (שפה משותפת-אחידה)
פירוש מילולי יותר של Ubiquitous הוא “נמצא בכל מקום בו-זמנית”. דומה מאוד ל Universal.
כיצד יוצרים מודל משותף, ומתוקשר היטב? – מדקדקים בשפה!
הרעיון של Ubiquitous Language הוא שנקודת מפתח לגיבוש, שימור, ועדכון המודל הוא הקפדה על שפה משותפת ואחידה – בין כל ה Domain Experts וכל המפתחים.
  • המודל הוא עמוד השדרה של השפה המשותפת. הוא אמור להכיל את המונחים החשובים.
  • אנו מקפידים על שמות נכונים של אובייקטים, טבלאות בבסיס הנתונים, משתנים וכו’ – ע”פ השפה המשותפת.
    • זה גם נכון למסמכים, מיילים, ודיונים. שימוש בשפה “תקנית” מצופה מכולם.
    • כאשר הקוד יוצא מסנכרון עם השפה – מיישרים אותו חזרה בעזרת Refactoring.
  • אם לקוחות משתמשים במונח הרבה – כדאי שזה יהיה אובייקט מרכזי במערכת. אם לא – זהו smell לטעות מידול.
  • אם לקוחות משתמשים במונחים דומים לחלופין – חשוב להבהיר את ההבדל! או שיש פה תובנה משמעותית – או שיתיישרו למונח יחיד.
  • בגדול, הלקוחות (ליתר דיוק: ה Domain Experts מהביזנס) הם אלו שמכתיבים את השפה. הם מעבירים את הידע שברשותם לפיתוח.
ללא שימוש פעיל ב Ubiquitous Language – המודל הופך לעוד מסמך Design שרק בדדים מכירים – ומושפעים ממנו.
Bounded Context
מה קורה כאשר Domain Experts בקבוצה אחת לא משתפים אותה שפה משותפת עם Domain Experts מקבוצה אחרת? למשל: אנשי הכספים לא שותפים לאותה שפה ותפיסת עולם עם אנשי המכירות? (בהנחה ששניהם לקוחות של המערכת)
פתרון אחד הוא ליישר צד אחד. זהו פתרון לגיטימי.
פתרון שני הוא להכיר בכך שיש שתי תפיסות עולם חשובות של הביזנס והשוק – ונכון לתאר את שניהם. במקרה זה אנו מגדירים Bounded Contexts – “הקשרים תחומים” שהם מעין יקומים מקביליים שלכל אחד מודל משלו, ושפה משותפת-אחידה משלו.
Bounded Context (להלן BC) הוא כלי שמומלץ לפרויקטים גדולים ומורכבים. להלן “Tackling complexity in the heat of software” – הסיסמה המתנוססת על ספר ה DDD.
הבחירה באימוץ כמה Bounded Contexts היא נכונות להשקיע עוד עבודה (ייתכן גם כפילות קוד / נתונים) על מנת ליצור לכל סוג לקוח את המודל שיניע את המערכת שהוא זקוק לה.
אם גם אנשי הכספים וגם אנשי המכירות מדברים על “נוסע”, אבל לכל אחד יש תכונות והתנהגויות שונות שלא ממש מסתדרות – אנו ניצור שני אובייקטים:
  • “נוסע” בהקשר התחום של אנשי הכספים.
  • “נוסע” בהקשר התחום של אנשי המכירות.
כמובן שגישה זו מזמינה בעיות Consistency במערכת (לא דבר טוב) – ולכן חשוב על כל סוג נתון מרכזי (שם, אימייל, מספר טלפון) – להגדיר Single Source of Truth, קרי איזה אובייקט (“נוסע-כספים” או “נוסע-מכירות”) הוא מקור האמת במידה של חוסר התאמה.
ייתכן ושדות אחרים, למשל “ערך גולמי לחברה” – יהיו לא תואמים. סביר להחליט שאנשי הכספים יחליטו שהפוטנציאל העסקי לחברה מנוסע מסוים הוא 4,000 ש”ח, ואנשי המכירות יחליטו שהוא 2,700 ש”ח. כל עוד הם מודעים לקיום הפערים ומאמינים בנחיצותם – זה לגיטימי. ייתכן וגם מחלקת הכספים וגם מחלקת המכירות יעבדו טוב יותר, כאשר לכל מחלקה יהיה מודל המותאם לצרכים שלה.
טבעי כמובן שיהיו שורה של נתונים באובייקט ה”נוסע” שקיימים במודל המכירות ולא קיימים במודל הכספים (וליהפך). התכונה הזו הופכת את המערכת שלנו לפשוטה יותר ליישום (על פני ניסיון לפתח מערכת “ERP” בה יש אמת יחידה בעולם).

Bounded Context ועולם ה Micro-services
גישה (פשטנית) מסוימת קובעת שכל מיקרו-שירות מגדיר Bounded Context משלו.
זה נכון: בעולם המיקרו-שירותים לכל שירות יש בסיס נתונים משלו, ויש כפילויות נתונים מסוימות – בהגדרה, וגם single source of truth.
לומר שכל שירות הוא Bounded Context זו נראית לי כשגיאה גדולה, הליכה רחוק רחוק מדי. סביר לנהל נתונים שונים בכל שירות – אבל לא נראה לי סביר לנהל עשרות או מאות מודלים ושפות אחידות. זה פשוט לא הגיוני.
יש לי שם למצב בו כל שירות מגדיר שפה משותפת-אחידה משלו: “כאוס”.
Context Map
כאשר יש כמה Bounded Context, חשוב למפות ולשתף את הידע אלו BCs קיימים, מה הגבולות ביניהם, ואלו אובייקטים משותפים (אך בעלי וריאציות שונות) הם חולקים. בסיס הידע הזה נקרא Context Map.
אין איזה פורמט חכם או שיטה מיוחדת לנהל את ה Context Map, עד כמה שזכור לי. כדאי שהניהול יהיה הגיוני ואינטואיטיבי.
Core Domain
עוד כלי לפרויקטים גדולים: כאשר המודל שלנו הולך וגדל – קשה להתמצא בכל הפרטים, במיוחד כאשר אנו רוצים שקבוצה גדולה של אנשים תעבוד ותתקשר על בסיס אותו מודל, ובצורה פרואקטיבית ומעמיקה.
הפתרון הוא להגדיר Core Model, “מודל ליבה” שמשותף לכולם – אך כולל רק את האלמנטים החשובים ביותר.
לאותו מודל יהיו הרחבות שונות, להם יהיו שותפים צוותים ספציפיים. סה”כ הרעיון הוא הפרדה בין החשוב ביותר (“מודל ליבה”) לחשוב מעט-פחות (“הרחבות למודל”).
כדי שמודל הליבה יהיה יעיל, כדאי (גם בפרויקטים קטנים) לחדד ולזקק אותו לכדי שלמות. שיהיה פשוט. מדויק. עדכני וקל להבנה. איכות המודל תכתיב את איכות יישום ה DDD, בתכלס.

כמה קשיים ביישום DDD

אם DDD היה כרוך רק בהתקנה של תוסף (נוסח apt-get install) – בוודאי רובנו כבר היינו משתמשים בו.
בפועל, זה קצת יותר מורכב.
  • קבוצת ה R&D לעתים נתפסת ע”י ההנהלה כמרכז עלות ולא כמרכז רווח: נכון, החברה אולי מרוויחה מהתוכנה שכבר כתובה, אבל הדגש כעת הוא על צמצום בעלויות הפיתוח (פחות עלויות תחזוקה, פיתוח פיצ’רים מהר / זול יותר) ולא בהשקעה להגיע ל”פיצ’ר שתופס בצורה מעמיקה את הביזנס”.
  • מפתחים שקועים במידה רבה בטכנולוגיה ובמחשבה כיצד להתנסות / לשלב טכנולוגיה חדשה במערכת – יותר מאשר איך ליצור תוכנה נכונה יותר לביזנס.
    טכנולוגיות חדשות הן מפתח להתחדשות והתייעלות, אך כאשר המיקוד הוא “אימוץ טכנולוגיה החדשה” ולא החדשנות וההתייעלות – התוצאה היא בהתאם.
  • ה DB כבר כמה עשורים מקבל מרכז כובד מוגזם בתכנוני המערכת. נכון: הוא כלי רב יכולות, וקל לפשט בעיות ועסקיות ולחשוב עליהן כבעיות של data (“כיצד לוקחים את הנתון הזה, מבצעים עליו עיבוד ומעבירים אותו לשם”) – אבל ההתייחסות ל DB כמרכז המערכת מסתירה מאיתנו תובנות עמוקות יותר על המערכת והביזנס, ולרוב מונעת מ “תכנונים יעילים במיוחד” של המערכת מבחינת DDD – מלהתפתח.
  • מבנה המערכת לא מתמפה בצורה פשוטה למודלים העסקיים. 
    • הסיבה: המפתחים בוחרים במידול / מבנה מערכת מתוך חשיבה טכנית גרידא. הם בוחרים בשמות לאובייקטים ולמודולים, כך שיהיו נוחים להם עם הידע הנוכחי שהם – “ידע” שלרוב הוא בעצם בורות עמוקה (סליחה סליחה!) ברזי הביזנס.
    • התוצאה: חלק מהדרישות העתידיות שיגיעו למפתחים יהיו “הפתעת יום כיפור”: הם תכננו את המערכת עם גמישויות X – אבל הדרישות סובבות מסביב לגמישויות Y. השינוי הנדרש הוא ארוך ויקר והמפתחים יעמדו על הרגליים האחוריות להסביר לביזנס ש”הדרישה שלהם לא סבירה”, וש”יאלצו להסתדר בלי”. הם עלולים להיות כמו כלב תוקפני שיש להיזהר ממנו.
      מותר להעמיד את אנשי הביזנס בפני אי נוחות – מלבד הפעמים שאסור.
    • תוצאה בגרסה “משופרת”: המפתחים עובדים בחשיבת Lean. הם לא יצרו גמישויות X כלשהן – ולכן “תסריט יום כיפור” הוא פחות מזעזע עבורם. הם פשוט יבצעו את השינויים הנדרשים במערכת על מנת ליצור גמישות Y. הם אדישים לשינויי הדרישות והם לא יעצרו או יילחמו בביזנס – אבל הם גם יכולים לבזבז זמן רב ב Refactorings הלוך ושוב – כי הם לא מצליחים “להריח” להיכן הביזנס לוקח אותם. הם כמו כלב ידידותי אך עיוור.
ה “System Flexibility Challenge” בין R&D לביזנס, מודגם במטאפורה של מכאניקה.
המפתחים מספקים גמישויות x1 ו x2 – ושמחים שענו לצרכי הביזנס, אבל הביזנס בעצם זקוק ל y4 ו y2.
קשיים בעבודה משותפת: פיתוח ו Domain Experts

Communications usually fails, except by accident”  — Osmo Wiio”

למעלה: איך שהפיתוח עשוי להיתפס לפעמים ע”י הביזנס – מקור מקור: nytimes
למטה: איך שהביזנס עשוי להיתפס לפעמים ע”י ה R&D – מקור: spiritscienceandmetaphysics
התקשורת בין המפתחים לאנשי הביזנס היא חלק מורכב – ומאוד חשוב ב DDD.

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

ה Domain Expert מבין עניין. לאחר שיחת היכרות של שלושים דקות הוא מסדר לנו את הדברים בראש ב Domain שלו. משם נפגשים עוד כמה פעמים, תוך כמה מפגשים – אנחנו כבר “מביני עניין” ב Domain! אנחנו מגדירים במשותף מודל – וזה ממש עובד יפה!. משם ממשיכים לפיתוח, אנחנו עושים את העבודה שלנו (נהדר, כרגיל) ויוצאת מערכת פצצה שמזניקה את הביזנס – וה Domain Experts אסירי תודה לעד!
הללויה!!

רק שזה לא עובד כך.

פערי השפה / תפיסה בין איש פיתוח ל Domain Experts הם גדולים – ולוקח זמן מה להתחיל ולגשר עליהם.

למד אותי את הדומיין!” היא קריאה שתעורר לרוב השתאות ממש כמו הבקשה “צייר לי כבשה!”
“הדומיין”? ה Domain Expert לא רגיל ללמד את תחום המומחיות שלו, לזקק את הידע הזה, ולהנגיש לאדם מתחום אחר. רוב הסיכויים שהוא ינסה לעשות דבר כזה פעם ראשונה.
רוצים להיות אנשי כספים? אין בעיה. נתחיל במשימות פשוטות ותוך חצי שנה, אם תתאמצו – תבינו עניין!
רוצים להבין את תמצית הידע, בחתך ששימושי לאנשי תוכנה (שאין לנו מושג כיצד באמת הם עובדים) תוך כמה פגישות? – קצת יותר קשה…
יכול להיות שה Domain Expert שלכם לא נראה כזה “מומחה”. מומחה עסקי הוא לא תמיד אדם אנליטי שיודע לחדד תובנות בזו אחר זו. לפעמים גם הוא בעל מומחיות בינונית.
יכול להיות שהמומחה הוא טיפוס טכני, ללא כישורי תקשורת או הוראה מפותחים במיוחד.
יש גם סיכוי טוב שיש פער אמון שצריך לגשר עליו. במיוחד אם ה Domain Expert הוא משתמש מתוסכל של מערכת מחשוב כלשהי שפותחה ב R&D…
הכל יכול להיות.
פעם אמרו ש eXtreme Programming לא מתאים לכולם.

אז גם DDD לא מתאים לכולם. הוא מתאים לגוף R&D שזו האג’נדה שלו, ולאנשי תוכנה בעלי כישורי תקשורת טובים, והרבה מוטיבציה להיכנס לעולם הביזנס – בהשקעה הנדרשת.
איך מכשירים אנשי תוכנה לעבוד עם Domain Experts? למדל?
אני בהחלט לא מסוגל לענות על השאלה הזו על רגל אחת, ואולי גם לא מסוגל בכלל 🙂

סיכום

כמו בפוסטי עבר: אם אתם מעוניינים רק לספר לחברים שאתם עושים DDD וזה “מה-זה מגניב!”, אתם יכולים להתעלם מכל הנאמר בפוסט – ופשוט להצהיר זאת כבר עכשיו. אני מרשה.

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

הפוטנציאל של DDD להשיג שיפור משמעותי במקצוע התוכנה – הוא גדול, במיוחד מסביב לאזור המידול. הבעיה: קשה למצוא את האנשים שיפיקו ממנו את המירב. ייתכן גם שהם לא העובדים הטיפוסים של סטאראט-אפ מגניב, שבדיוק מחלקים את הזמן בין משחק בין Vue.js ו Docker.

אציין משהו לא מגניב: אני נתקל בפעילות רבה במיוחד בכל הנוגע ל DDD דווקא בעולם הדוטנט / #C (למשל: naked objects – אבל גם הרבה מעבר לזה). אני מניח שמפתחים הרבה יותר “תוכנה עסקית” על סביבת NET. של מייקרוסופט מאשר על סביבות אחרת – או לפחות הם מזהים את עצמם ככאלה.

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

למה אני אומר את זה? – אני זקוק לפוסט המשך בכדי להסביר…

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

הכנס השני לארכיטקטורת תוכנה (30 לנובמבר – 1 בדצמבר)

בסוף החודש הקרוב, 30 בנובמבר – 1 בדצמבר, יתקיים בהרצליה הכנס השני לארכיטקטורת תוכנה.

הכנס מאורגן ע\”י IASA ואילתם.

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

ILTAM, היא קהילה מקצועית, רחבה יותר, המורכבת בעיקר מחברות הייטק גדולות ומבוססות (לא רק חברות תוכנה נטו: אלתא, 3M, וצה\”ל למשל – הם חברים) במטרה לקדם ולשתף ידע.

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

היום ראשון מורכב מהרצאות. הנה התכנית:

ביום שני מתקיים Tutorial בהדרכתה של Rebecca Wirfs-Brock (שהגיעה לארץ במיוחד, אני מניח), בנושאי ארכיטקטורה באג\’ייל ו Quality Attributes בפרט (הנה פוסט שפירסמתי בנושא, אם אתם רוצים לקבל מושג במה מדובר).

האם כדאי לבוא?

תקשיבו, זו שאלה דיי אינדיבדואלית, ואני בד\”כ זהיר במתן המלצות. בכל זאת, כשאני מסתכל התכנים – נראה לי שהצליחו לרכז באמת שורה של נושאים ומרצים מעניינים ביום הראשון – שרלוונטיים לאנשי-תוכנה כמעט מכל הסוגים.
היום השני הוא באמת ממוקד יותר לארכיטקטים, או מי שרוצה שהתעמק בטכניקה תאורטית שהתמורה שלה להשקעה היא ארוכת-טווח. אני לא יודע להמליץ ספיצית על ה Tutorial שנבחר, אבל אם הוא נבחר באותה רוח של בניית האג\’נדה ליום הראשון – ייתכן בהחלט וזה יהיה Tutorial מוצלח!
  • אני נותן הרצאה על מיקרו שירותים (עדיין לא החלטתי בדיוק איך להעביר את הנושא…). אם אתם מגיעים – קפצו לומר שלום!
  • קצת אחרי ייתן הרצאה יונתן ממן, שכתב כאן פוסט אורח בנושא – ממש לאחרונה.
  • הנה אתר הכנס: http://conference70.wix.com/sw-architecture
  • בטופס ההרשמה שבאתר, ניתן לקבל הנחה אם מזינים את המילה \”Presenter\”, בסעיף של קבוצת שיוך (אופס, אני מקווה שבאמת היה מותר לי לספר את זה…).
שיהיה בהצלחה!
ליאור
 

כל מה שרצית לדעת על ארכיטקטורת מיקרו-אפליקציות (Micro-Apps Architecture) – ולא העזת לשאול

פוסט זה נכתב ע”י יונתן ממןמנהל קבוצת פיתוח באאוטברין על ארכיטקטורה בה היה שימוש במערכות של אאוטברין.


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

Fox News, ה Rolling Stone, “הארץ”, “בלוג ארכיטקטורת תוכנה” (בדיחה, בדיחה), ועוד. אאוטבריין מייצרת כ-+150 מיליארד המלצות תוכן בחודש – כמות Traffic יוצאת דופן! בזירת ההייטק הישראלית היא נחשבת מובילת-דרך בכל הנוגע ל Scale, Operations, וכו’. החברה גם פעילה למדי בקידום אירועים ושיתוף ידע בתעשייה (IL TechTalks, רברסים וכו’).

 

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

  • בסיס קוד קטן, פשוט וקריא. כל service מבצע מספר קטן של משימות, ויש לו אחריות מאוד מוגדרת.
  • ממשק וגבולות ברורים לכל service, ולכן יש גם בעלות ברורה שעוזרת לשיפור האיכות.
  • כל service נכתב בטכנולוגיה שמתאימה לו ולצוות שאחראי עליו.
  • ה service עולה יורד באופן מהיר יותר, ולכן מתאים מאוד לפריסה מתמשכת (Continuous Deployment).
  • משפר את יציבות מערכת – אם service אחד נפל, service-ים אחרים ממשיכים לתפקד. אם service אחד עמוס אפשר בקלות להתקין שרתים נוספים שיריצו את אותו השירות.
ה tutorials שתמצאו, כנראה ידברו על שירותי רשת (web services) – ללא UI ובעיקר ללא ניהול session. אבל מה עם אפליקציות רשת (web applications), שם ה UI, וניהול ה session של המשתמש הם החלקים המרכזים ? האם גם אותם ניתן לפצל ל ״מיקרו-אפליקציות״ ? נניח שלקחת אפליקציה אחת ובדרך קסם פיצלת אותה לשניים, איך תתמודד עם:

  • שמירה על חווית כניסה (login) אחת?
  • מעבר חלק, ושיתוף session בין האפליקציות?
  • מניעת שכפול קוד ?
בוא ניקח לדוגמה אפליקציית רשת נפוצה: אתר של חברת סטארטאפ.
כמו שכל בית צריך מרפסת, כל סטארטאפ  צריך אתר. מה יש באתר? ובכן, זה לא רק אתר שיווקי. זהי אפליקציית רשת שמאפשרת ללקוחות להשתמש במוצר ולתפעל אותו בשירות עצמי. למשל: המשתמשים יכולים לקבוע פרמטרים, לשנות הגדרות, להוריד דוחות וגרפים. יש גם כמובן מנגנוני רישום והתחברות, הגדרת פרופיל משתמש, שחזור והחלפת ססמה, CAPTCHA, וכו’.
האתר לרוב יהיה מהמערכות הוותיקות בחברה – ולכן גם עם חוב טכנולוגי גבוה / Technology Stack ישן.
לכאורה מועמד מצוין לשדרוג ולמעבר לארכיטקטורת מיקרו services, אבל אליה וקוץ בה: דווקא אפליקציית רשת קשה מאוד לפצל ויש מעט מאוד מידע ברשת כיצד לעשות זאת.
פוסט זה יתאר ארכיטקטורת מיקרו-אפליקציות (Micro-Apps Architecture) שמטרתה (בדומה ל Micro-Services Architecture) לסייע בפיצול אפליקציית רשת למיקרו אפליקציות ובכך ליהנות מהיתרונות המדוברים.
אז איך עושים את זה ?
בשלב הראשון ננתח את המבנה הלוגי של האפליקציה. סביר להניח שאפליקציה שמשרתת כמה וכמה תרחישים, תורכב מכמה אזורים עיקריים.

כל אזור (אדום, צהוב או כתום) הוא מועמד להיות מיקרו אפליקציה עצמאית.
בשלב השני נוציא כל אזור מתוך האפליקציה ונהפוך אותו למיקרו אפליקציה. מהי מיקרו אפליקציה? מיקרו אפליקציה היא אפליקציה  שאחראית על פונקציונליות ברורה מוגדרת ומצומצמת. לדוגמה משרתת סוג לקוחות מסוים, או חושפת יכולות של מוצר מסוים משלל המוצרים של החברה.
כשבאים לקבוע את הגבולות בין האפליקציות ניתן להשתמש באותם שיקולים בהם משתמשים בחלוקת service , למיקרו .services.
המיקרו אפליקציה בנויה מטכנולוגיה המתאימה לה ולצוות שלה ומותקנת על שרת ייעודי. האפליקציה יכולה להגיש גם את צד הלקוח (client side) וגם את צד השרת (server side). האפליקציה מותקנת בתוך ה firewall ולא מקבלת תעבורת רשת באופן ישיר.
micro-colored.png
נייצר service מארח (hosting service)  דרכו עוברת כל התעבורה למיקרו אפליקציות. ברוב המקרים ניתן להשתמש באפליקציה המקורית (המונוליטית) כ service מארח, כיון שאחרי שהוצאנו מתוכה את רוב הפונקציונליות כנראה שנותר שם רק התשתית של ניהול הsession, אימות המשתמש, אבטחה וכו’.
ה service המארח משמש כמעין נתב. ״נתב על סטרואידים״, אם תרצו.

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

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

מה עושה הפרוקסי?

  • הפרוקסי הוא יחידה לוגית בתוך ה service המארח. תפקידו להוות צינור ולהעביר בקשות מהלקוח למיקרו אפליקציה עליה הוא אחראי. התקשורת בין הפרוקסי למיקרו אפליקציה באמצעות פרוטוקול HTTP. לכל מיקרו אפליקציה קיים פרוקסי משרת אותה.
  • כל פרוקסי נרשם על מסלול אחר של ה URL. לדוגמה כל בקשה למשאב תחת הכתובת http://www.yoursite.com/app1 תופנה לפרוקסי האדום.
  • כשהבקשה מגיעה לפרוקסי כבר ידוע מיהו המשתמש, ולכן הוא יכול להעביר את שם המשתמש (או כל מידע אחר עליו) למיקרו אפליקציה באמצעות HTTP Header.
  • הפרוקסי מנהל את כמות החיבורים הפתוחים לאפליקציה שלו, ומדווח על כמות החיבורים הפתוחים למערכת הניטור.
  • הפרוקסי מקבל בחזרה את התשובה מהמיקרו אפליקציה ומעביר אותה חזרה למשתמש.

יתרונות – או מה יצא לנו מכל זה?

  • הפרדה ברורה בין אפליקציות: זליגת זכרון במיקרו אפליקציה אחת לא תביא לנפילה של מיקרו אפליקציה אחרת.
  • ה service המארח הופך מהר מאוד לתשתית שבה אין הרבה פיצ’רים חדשים. הוא הופך לפשוט וקל יותר לתחזוקה.
  • כל מיקרו service נכתב בטכנולוגיה שמתאימה לדרישותיו ולצוות שמפתח אותו.
  • ניתן לעשות את המעבר מאפליקציית רשת מונוליטית למיקרו אפליקציות בשלבי, ובכך להקטין את הסיכון. לדוגמה:
    • לייצר מיקרו אפליקציה שתגיש רק את קוד הלקוח שנכתב בטכנולוגיה חדשה, נניח ב AngualrJS, ולהשאיר את קוד השרת ב service המארח.
    • לייצר מיקרו אפליקציה חדש שיגיש קוד שרת בלבד. ניתן להחליט שלא מעבירים קוד ישן אלא רק קוד חדש של פיצ’רים חדשים. וכך לבצע החלפה אטית אך שיטתית ומדורגת מתשתית שרת ישנה לתשתית שרת מודרנית יותר.
  • ניטור — כל פרוקסי מדווח על כמות הכישלונות, הצלחות, מספר החיבורים התפוסים וכו’.
  • רישום בקבצים – ה service המארח אחראי על כתיבת קבצי הגישה access log עבור כל המיקרו אפליקציות.
  • אבטחה — ה service המארח מבצע את כל מנגנוני האימות של כניסת משתמשים ו CAPTCHA.
  • אפליקציות חדשות — נניח שאתה רוצה לפתח אפליקציית-רשת חדשה. תוכל לממש אותה כמיקרו אפליקציה בתוך ה service המארח. כך תחסוך בפיתוח מנגנוני אבטחה, רישום וכניסה, כתיבת קבצי גישה וכו’, כל מה שתדרש זה להוסיף עוד חוק ניתוב service המארח. בנוסף לא תצטרך את עזרת אנשי ה OPS. גישה זו היא מיושרת עם רעיון ה DEVOPS.
כמובן שאין ארוחות חינם. אז מה אנחנו מפסידים?

  • עוד תחנה במסלול — טיפול בבקשה של משתמש לוקחת זמן ארוך יותר.
  • שיתוף קוד לקוח — כבר לא תוכל לשנות בקלות את ה JS/CSS שמשותפים עבור כל המיקרו אפליקציות. בכדי לשתף קוד לקוח (למשל ניווט בתוך האפליקציה), תצטרך להתייחס אל הקוד כאל ספרית צד שלישי. תוכל להשתמש ב bower בכדי לנהל את הגרסאות ולהעלות את הקוד ל repository. דרך נוספת תהיה להעלות קבצים משותפים ל CDN.
  • הגדל הקושי בתחזוקה ובמציאת תקלות – כל תוספת של חלק נע במערכת מקשה על מציאת באגים. עוד קבצי לוג שצריך לבדוק כמשנסים לאתר תקלה. נניח שמשתמש מנסה לגשת למיקרו אפליקציה ומקבל HTTP CODE 403. יהיה עליכם לבדוק האם ה service המארח חסם את הגישה, או אולי האבטחה של המיקרו אפליקציה חסמה את הבקשה.
  • הפרוקסים הם פשוטים ולא משוכללים כמו HAProxy.
הניסיון שלנו באאוטברין:

  • פתחנו את הארכיטקטורה הזו בתחילת 2015, כאשר הבנו שאנחנו הולכים לחשוף הרבה יכולות של המוצר שלנו למשתמשי הקצה באמצעות שירות עצמי דרך אפליקציית רשת. אפליקציית הרשת שלנו הייתה מבוססת על תשתיות ישנות והבנו שמבחינה טכנולוגית אנחנו צריכים לבצע שדרג משמעותי.
  • המטרה הייתה לפתח יישומים חדשים בטכנולוגיות מודרניות באופן בלתי תלוי ע”י צוותים שונים עם סט בדיקות שונה תוך כדי שיתוף session בין האפליקציות, כל זאת בלי לכתוב מחדש את כל התשתית של אפליקציית הרשת הקיימת.
  • התחלנו עם מיקרו אפליקציה אחת, ועכשיו יש לנו 4 מיקרו אפליקציות שמגישות גם צד שרת וגם צד לקוח. הנה דוגמה ל Stack הטכנולוגי של 3 מארבעת מיקרו אפליקציות בתשתית של אאוטברין:
  • לכל מיקרו אפליקציה יש מערכת בדיקות עצמאית וההחלטה האם אפשר לעשות Deploy ל production נמצאת בידי הצוות שמפתח את האפליקציה.
  • לכל מיקרו אפליקציה יש ניטור משלה וההתראות מגיעות לצוות הרלוונטי.
  • הוספנו ניטור ל service המארח בכדי לוודא שכמות החיבורים לכל מיקרו אפליקציה לא עוברת ערך מקסימלי וכמות השגיאות וההצלחות.
  • קובץ הגישה ב service המארח חושף את כל הפעילות של המשתמש בכל המיקרו אפליקציות השונות ונותן מבט אחד על פעולות המשתמש.
  • הוספת מיקרו אפליקציה נוספת היא מאמץ קטן של מספר שעות פיתוח.
  • כמות השינויים שבוצעו במערכת הישנה היה קטן יחסית, וזמן הפיתוח היה קצר – ולכן הסיכון שבמאמץ הפיתוחי היה נמוך.

ארכיטקטורה של Hyper-Scaling

נושא ה Scalability הוא פופולרי-במיוחד בכמה השנים האחרונות.לאחרונה עברתי על עשרות קורות חיים של מועמדים – ובכמעט כולם צוינו מושגים כמו “High Scalability”, או “Big Data”, “NoSQL”, “Hadoop” – וכו’. כנראה שכל מי שעבד במערכת עם הרבה transcriptions per seconds או נפחים גדולים של נתונים – סיפר על כך בהרחבה, ומי שלא – התאמץ להראות איזו זיקה. זה “המשחק” היום, ונראה לי שהייתי עושה בעצמי את אותו הדבר בדיוק!

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

No Scale

אני רוצה להזכיר שהמונח “Scalability”, מתייחס בהנדסת תוכנה לשני סוגים של אתגרים:

  • Software Scalability – התמודדות עם יותר משתמשים, יותר פעילות, יותר נתונים.
  • Development Scalability – היכולת להתנהל עם צוות פיתוח גדול יותר.
ב Gett יש לנו Software Scale מסוים, שהוא לא קטן – אבל גם לא ענק. ככה וככה נתונים, ככה וככה פעולות בשנייה.
ההתמודדות העכשווית שלנו היא דווקא יותר עם Development Scalability, שכמו שאנסה להראות במהלך הפוסט – יש לה דמיון לא-קטן ל Software Scalability.
לפני כחצי שנה, כשהגעתי ל Gett היו בצוות צד-השרת כשישה מתכנתים. הגעתי מעט לאחר גיוס ענק של 150M$ שהחברה ביצעה. עם הגיוס, החברה החליטה להגדיל משמעותית את קבוצת ה R&D – בכדי לקבל משמעותית יותר תפוקה. בעת כתיבת הפוסט יש לנו כבר עשרים וחמישה (!!!) מתכנתי צד-השרת – ואנחנו עוד מגייסים.את הכלל של “לא להגדיל גוף פיתוח ביותר מ 50% בשנה” – שברנו כבר מזמן… מה עושים עכשיו? ואיך מתמודדים עם זה מצד הארכיטקטורה?

Scale

ההקבלה בין Scale של תוכנה ו Scale של קבוצות-פיתוח

נוסחה מקובלת בעולם ה Software Scale היא זו:
אנו מגיעים ל Scale כאשר יש לנו כמות משאבים (למשל: שרתים) מסוימת, וכל שרת מבצע עבודה בקצב מסוים.
גדילה ב Scale, כלומר: Scaling – מתבצעת ע”י הוספת שרתים או לחלופין ע”י שיפור הביצועים של כל מחשב בודד במערכת.
ככל שהמערכת גדלה – סביר שנחווה מצב בו כל מחשב נוסף שאנו מוסיפים הוא פחות יעיל מקודמו. מדוע? מכיוון ש:
  • פעולות על כמות גדולה יותר של נתונים – אורכות יותר זמן. למשל: אינדקסים בבסיס נתונים רלציוני, הפחתת הרציפות בדיסק, caches פחות יעילים או סתם פעולות joins גדולות יותר (merge על work set גדול יותר).
  • יותר תקשורת שנדרשת בין המחשבים השונים במערכת. יש הבדל בין הודעות עדכון שנשלחות ל 6 מחשבים – וכאלו שנשלחות ל 25 מחשבים. פעם נתקלתי במערכת שהוספה של מחשבים למערכת, מעל 16 מחשבים, כבר לא הגדילה את ה scale – בגלל ריבוי של הודעות כאלו.
  • כמות הקוד שלא ניתן למקבל (parallelism) באופן טבעי תגדל, ולא תקטן – אלא אם הייתה השקעה משמעותית בצמצום קוד שכזה.
  • חוסרי יעילות שונים – שצצים במערכת באקראיות טבעית.

באופן דומה, גם בגדילה של גוף פיתוח – המפתח האחרון שנוסף נוטה להיות (בממוצע!) פחות יעיל מקודמו:

  • כל עבודה רוחבית במערכת (למשל: Refracting גדול), הופכים להיות קשים וארוכים פי כמה – כאשר כמות הקוד גדולה יותר.
  • יותר תקשורת וסנכרון נדרשת בין המפתחים בקבוצה. אם פעם היה מספיק להרים את הראש מבהייה במסך – בכדי ליצור קשר עם מתכנת שמכיר היטב היבט מסוים של המערכת, היום כבר צריך לקום מהמקום, לחפש – ולעתים לגלות שצריך לדבר עם כמה אנשים בכדי לקבל תמונה מלאה.
  • תמונות-העולם של המפתחים בארגון מתבזרות במהירות: בניגוד לצוות שהיה לו זמן להתגבש במשך תקופה ארוכה – כעת יש זרימה של אנשים חדשים, שכל אחד רגיל לשיטות שונות וגישות שונות.
    הגישות הללו, עבור כל אחד, “הוכיחו את עצמן מעל ספק סביר – בעבר”. אמת. אבל מה עושים כאשר הגישות הפוכות זו לזו? האם ORM הוא טוב להכל, טוב רק לקונפיגורציה, או “רעה-חולה שיש להסיר מהמערכת בהקדם האפשרי!”?
  • יותר ידיים עובדות => יותר קוד => מערכת מורכבת יותר. כל שינוי במערכת מורכבת יותר – אורך יותר זמן בעצמו (מגמה לחוסר יעילות מובנה).
  • נוצרים יותר צווארי בקבוק (“רק משה מכיר את הקוד הזה“) – שהולכים ומקשים יותר ויותר על התקדמות הפיתוח.
  • יותר ישיבות, יותר המוניות, יותר אנשים שיש להכיר ולהתרגל לעבוד איתם – חוסרי יעילות שונים, שצצים במערכת באקראיות כל-כך טבעית.
ההשקעה ב Development Scale בפיתוח אמנם עוסקת במידה רבה בגיוס עובדים (“Capacity”), אבל לא פחות מכך – בשיפור היעילות של כל עובד (“Performance”). תהליכי ה Continuous Integration (ליתר דיוק: on-going integration) – מוגדרים מחדש, אנו משקיעים יותר בשיתוף הידע – ופישוט שלו, וכאן יש לצוות הארכיטקטים תפקיד חשוב.
אנו חותרים ל Continuous Delivery (הפעם: באמת) – בכדי לשפר את יכולת התגובה לתקלות במערכת, וכדי לעשות אותה יציבה יותר. באופן פרדוקסלי משהו, הניסיון בתעשייה מראה שדווקא העלאת תכיפות ה deployments מגדיל את יציבות המערכת – בטווח הבינוני-ארוך. יותר deploys = יותר “שבירות”, אבל אז גם יותר לקחים, יותר מנגנוני-התאוששות ובקרה, ויותר אוטומציה. כל עוד אין מנגנון ארגוני שמותיר “לעצור את הקצב, ולצמצם את קצב ה deploys” – האנרגיות ינותבו לשיפור המערכת, ומנגנוני הייצוב שלה.
ב Software Scale, יש את השאיפה התמידית ל Linear Scalability: האידאל שלפיו הוספה של כל מכונה למערכת, תתרום את החלק היחסי שלה. למשל: הכפלת כמות השרתים – תכפיל את הספק העבודה (למשל: כמות בקשות בשנייה).
לא ממש Linear Scaling: ככל שמספר הבקשות עולה – יש להוסיף חלק יחסי גדול יותר של שרתים בכדי לענות על הביקוש.
יש במערכת הזו צווארי בקבוק מסוימים ל scalability.
בקבוצת ה R&D כולנו מבינים שככל שהמערכת גדלה – היעילות של המתכנים הולכת וקטנה. אין לנו שאיפות ל Linear Development Scalability. אנחנו גם מכירים במגבלות המקבילות האנושית (“תשע נשים לא יכולות ללדת ילד בחודש אחד”).
בשונה מאיתנו ל Business דווקא יש ציפיות ל Linear Scalability – מפורשות יותר או פחות.
“פי-2 אנשי support עונים לפי-1.9 קריאות במוקד?” – הם מספרים, “כן… אנחנו מבינים שהנדסה זה קצת יותר מורכב. הגדלנו את הפיתוח פי 4, ואי-אפשר לקבל פי-4 פיצ’רים – אבל גם אי אפשר כבר לצפות לפחות לפי-3 יותר פיצ’רים, או לקבל אותם לפחות פי-3 יותר מהר?”הלחץ מצד הביזנס הוא אולי משני, אבל הוא משפיע – וגורם לנו להתייעל יותר בכל הנוגע ל Development Scalability של הפיתוח. בעיקר ע”י צמצום חוסר-היעילות שהמערכת יוצרת למפתח הבודד.

ב Software Scale, יש “קסם” שיכול לסייע למערכת לצמוח ב Scale שהוא יותר מלינארי: מצב בו פי-2 שרתים, משרתים יותר מפי-2 משתמשים. כיצד זה קורה? יש כמה דרכים, אבל ה”קסם” הנפוץ ביותר הוא Cache (או memoization – בגרסה התאורטית שלו).
כאשר אנו יכולים לבצע חישוב מורכב רק פעם ב 5 דקות, ואז להפיץ את התוצאות לעוד ועוד משתמשים – כמות גדולה אפילו יותר של משתמשים תגדיל את הלחץ רק על ערוץ ההפצה (CDN?) – ולא על יצירת התוכן (החישוב).

ככל שנתכנן את המערכת שלנו בצורה בה ניתן יהיה להשתמש יותר ויותר ב Caches שכאלו – נשפר את ה Scalability של המערכת. תכנון שכזה כולל, הרבה פעמים – משא ומתן עם אנשי הביזנס (“תקבלו את זה מעודכן פעם בשעה – לא כל הזמן”).

(פתרונות אחרים כוללים העברת עבודה ל Clients, או צמצום העבודה לקצב קטן מבו עולה ה scale, למשל: ביצוע חישוב על 500 משתמשי-מדגם, ללא קשר למספר כלל-המשתמשים במערכת).

Scaling שהוא טוב מ Linear-Scaling: הוספת שרת למערכת – מוסיפה יכולת לספק קצת יותר משתמשים מחלקו במערכת.

ב Development Scaling יש גם כמה “קסמים” שכאלו. הבולט בהם – הוא code re-usability: היכולת להשתמש בקוד שנכתב בעבר – עבור פיצ’ר חדש.

פתרון שונה-דומה הוא Generalization: כתיבת קוד כללי יותר – שיכול לשרת מטרות דומות, אך שונות.

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

מהנדסים צעירים, ואולי אף מהנדסים בכלל – נוטים לבצע הערכת יתר (גסה?) ליכולת שלהם לייצר קוד יעיל לשימוש חוזר / קוד כללי יעיל. משם נוצר הכלל You Ain’t Gonna Need It (בקיצור: YAGNI) המציע פשוט לא לנסות לחזות מקרים אלו מראש, אלא לעשות Refactoring לקוד כללי רק ברגע שהוכח הצורך – מעל ספק סביר.

בכל מקרה: שימוש חוזר בקוד והכללה, גם אם נעשים בדיעבד – הם כלים חשובים מאוד לשיפור ה Development Scalability.

אז מה עם ארכיטקטורה ל Hyper-Scaling?!

אולי אתם מאוכזבים מעט מהפוסט: יש בו הרבה דיבורים כללים, ואין בו Hadoop, HPC או Big Data משום צורה!

אני אנסה לתמצת:
הפיתוח של Gett עובר כרגע תהליך של Development Hyper-Scaling. יש גם בעיות של Software-Scaling – אבל הן (עדיין) פחות מאתגרות – אולי אזכיר לקחים משם בפוסט אחר.

הארכיטקטורה, או תוכנית-העל שלנו להתמודד עם בעיות ה Development Hyper Scaling הן כאלו:

  • בראש ובראשונה – מעבר ל Micro-Services: הפיכת מערכת אחת מורכבת – לכמה מערכות קטנות יותר, ומורכבות פחות. המעבר הוא אינטנסיבי, אבל הוא מאפשר להבין ביתר קלות את כלל המערכת – ולדעת להיכן לצלול בעת הצורך. כמו כן – הוא מצמצם במידה רבה את הצורך בתקשורת מרובה, לטובת תקשורת בסיסית יותר וממוקדת יותר, למשל: סך האחריויות של כל שירות, וה APIs שלו – שמוגדרים היטב (אנחנו משתמשים ב Swagger לתיעוד – כלי שמשרת אותנו היטב).
    את השימוש ב MSA להתמודדות עם Development Hyper-Scaling לא המצאנו בעצמנו: למדנו מ case-studies על חברות שעמדו באתגר דומה (למשל: אמזון).

    • שימוש-חוזר בקוד, הוא רעיון שקשה לממש (מעבר לפונקציה פה ושם). דווקא Micro-services, בכך שאנו מגדירים שירותים עם שימוש עסקי ברור, ו APIs מוגדרים היטב – מסייעים לנו ליצור יחידות גדולות של קוד שמתאימות לשימוש-חוזר. כבר בחצי-שנה האחרונה, היו לנו כמה הצלחות יפות.
  • אנו עוסקים בצורה פרואקטיבית בארגון בשיתוף ידע על חלקי המערכת השונים, האחריויות שלהם, וה flows העיקריים במערכת. לא עובר כמעט שבוע שאני לא עושה session שכזה, לצוות כלשהו בפיתוח – ואני לא היחידי. עוד פעם ועוד פעם – עד שלכולם כבר יימאס (אנחנו עוד רחוקים משם…).
    שמות פשוטים, מטפורות טובות, וסיפורים קליטים – הם מרכיב עקרי בבניית והפצת הידע.
  • צוות הארכיטקטים לוקח תפקיד קצת יותר ריכוזי מהרגיל (אולי: יותר מהאידאל האישי שלי?!) בהגדרת superflows חדשים במערכת. כן! אנחנו רוצים לעבוד יותר agile ולתת לאנשים יותר ויותר אחריות והשפעה, אבל בנקודת הזמן הזו – תוצאות טובות יותר מושגות כאשר לפחות את ה flows העיקרים – מוגדרים מרכזית ע”י הארכיטקטים.
    כאשר מפתחים עושים שינויים ושיפורים ב flows – זו סיבה לשמחה (אלא אם בכך הם סותרים עקרונות או flows אחרים במערכת).
  • אנו מנסים לקדם בקוד כמה עקרונות:
    • קידום תרבות של הצגת פתרונות – ולא רק בעיות (בכל ה R&D).
    • קוד פשוט להבנה – עדיף יותר על פני קוד קצר או מתוחכם. אם אתם קוראים של הבלוג זמן רב, אתם אולי יודעים שזו הנטייה הטבעית שלי – אבל זה לא הסטנדרט הברור של ריילס (שם עקרונות של קוד קצר ו DRY – מושרשים עמוק בקהילה).
    • יותר כלי monitoring ו supportability עבור הפיתוח. בכתיבה של כל פיצ’ר – לחשוב איזו השקעה תהיה משתלמת כאשר לפיצ’ר הזה תהיה בעיה ב production. כלי supportability יכולים להציג חווית שימוש עלובה למדי – כל עוד הם עוזרים.
    • הכנסה של אוטומציה / בדיקות יחידה / בדיקות-API / בדיקות-אינטגרציה. בכל ארגון שראיתי בעשור האחרון זו הייתה המגמה – אבל אנחנו עכשיו צריכים את זה יותר.

אני מודע לכך שעדיין אין פה Design Patterns הנדסיים משמעותיים (אולי מלבד MSA) – אבל זה מה שעובד, ואנו עושים את מה שעובד – ולא רק מה שמתאים לציפיה מסוימת (ארכיטקטורה = “תרשימים של ריבועים”). סורי! 🙂

זהו… מקווה שנהניתם, ואולי אף השכלתם. כרגיל – אשמח לתגובות.

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