על אבולוציה של ארכיטקטורה ו Conway's Law

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

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

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

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

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

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

נתאר את המצב הצפוי הבא, בצוות האחראי על פונקציונליות x:

  • את הקוד שהצוות אחראי עליו (להלן: "מודול A") – יהיה קל ונגיש לשנות.
  • את הקוד שאחראי עליו צוות אחר (להלן "מודול B") – יהיה מורכב יותר לשנות: צריך לתאם, לתקשר יותר, ולהשתלב בעבודה של הצוות השני.

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

חוק קונווי

חוק ידוע בעולם התוכנה הוא Conway's Law שטבע איש מדעי-המחשב מלווין קונווי במאמר משנת 1968. למרות שזמן רב עבר – החוק עדיין נכון ולרלוונטי מתמיד.

החוק אומר כך:

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

אם היה לנו תכנון (משמאל), וניסינו ליישם אותו בארגון עם מבנה מסוים (בירוק במרכז), נגזר עלינו שהתוצאה הסופית / היישום של התכנון יהיה יציר כלאיים בין התכנון המקורי, לצורת התקשורת בארגון (מימין).

בואו ואמקד למה אני מתכוון כאשר אני מדבר על "מבנה התוכנה" או "הארכיטקטורה":

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

ההכרה בחוק מותירה לנו 3 ברירות עיקריות:

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

התאמות כגון:

העברת אחריות של מודול m מאחריות של צוות a לאחריות צוות b.
ייתכן והמודול עדיין רלוונטי למשימות והמומחיות של צוות a – אבל צוות b יידרש לעבודה אינטנסיבית באזור הזו, ועדיף שחלק הקוד הזה יהיה מעתה באחריותו. העברת אחריות על פיסת קוד בין צוותים היא לכאורה שינוי ניהולי שאינו קשור לארכיטקטורה, מלבד עובדה אחת: העובדה שהצוות הקודם (צוות a) עדיין צריך חופש מסוים לבצע שינויים ותוספות במודול m.
כיצד מאפשרים לצוות a את היכולת לבצע שינויים בקוד שרוב העבודה עליו נעשית ע"י צוות b – ועבור מטרה אחרת?
האם צוות a יאפיין ויגיש דרישות – בכדי שצוות b יממש אותן בעבורו (סוג של פתרון ניהולי עם תקורה גבוהה)?
אפשרות אחרת היא מעבר הדרגתי ל Plug-In Architecture בו מבנה התוכנה מאפשר לצוות a להמשיך ולפתח חלק מכובד מהפונקציונליות שהוא נדרש לה – ללא תלות בצוות b, וכך לשפר את מרחב-הפעולה העצמאי שלו.
PlugIn Architecture

פיצול של מודול ל-2: חלק שיישאר בצוות a, וחלק שיעבור לצוות b

אנו מתחילים כרגע לבחון את המשמעות של פיצולים שכאלה. לעתים הם לא פשוטים: הם עשויים לגרום לשכתוב של קוד ולפגיעה מסוימת בביצועים (בעיקר בעיות Latency, בגלל שאנו עובדים בארכיטקטורה של מיקרו-שירותים. כלומר: פיצול מיקרו-שירות ל-2 שירותים, ע"פ הצרכים של הצוותים השונים).
בעיה שלישית שיכולה לצוץ היא סיבוך של flow מסוימים: שירות שלישי שצריך כעת לעבוד עם שני שירותים שונים במקום אחד, או פגיעה באטומיות: מה שעד היום התרחש בצורה אטומית בתוך שירות אחד מעכשיו ידרוש coordination בכדי לא לסיים במצב בו רק חצי עבודה נעשתה.
היכן נכון לפצל? מה יהיה ממשק העבודה ביניהם וכיצד הפיצול ישתלב ב Flow? – כולן שאלות טובות.עוד עניין שצץ מחלוקה שכזו הוא עניין של פלורליזם: האפשרות ששני השירותים יכירו תמונות עולם מעט שונות של המציאות, כל אחד ע"פ צרכיו. למשל: אובייקטים המתארים אותו הדבר ("לקוח") יכילו שדות שונים בכל שירות. זה אפשרי כל עוד במידה ויש כפילות – ברור מהו המקור שאליו מתייחסים. כלומר: אם מספר הטלפון של איש הקשר של לקוח מסוים שונה בין שני השירותים – חשוב שיהיה ברור איזה שירות הוגדר כ"קובע העובדה" – והשירות השני יתיישר איתו.
עקרון זה הוא נפוץ ב Microservices, אבל פחות מקובל בארכיטקטורות ריכוזיות.

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

סיכום

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

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

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

לינקים רלוונטיים

על השינוי של מייקרוסופט לחברת ענן

כנס הארכיטקטורה הראשון של IASA ו ILTAM

עדכון (11 בדצמבר, 2014)

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

——

שבוע שעבר, בימים שני ושלישי התקיים \"כנס הארכיטקטים הראשון\" של IASA ואילתם.

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

ל IASA יש גם קבוצה ב LinkedIn  (דורשת הצטרפות) – בה חברים רבים מהארכיטקטים שאני מכיר. אם אתם ארכיטקטים – אני ממליץ לשקול להצטרף (אני חושב שלא יאשרו לכם להצטרף אם לא כתוב בקורות החיים שלכם שאתם ממש ארכיטקטים באופן רשמי).

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

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

אני חושב שלשתי הקהילות יש מה ללמוד אחת מהשנייה. באופן אולי מפתיע, אני חושב שדווקא ל\"תעשיה המסורתית\" (מסורתית = חברות שחיות 15 שנה או יותר) יש יותר מה ללמוד מסצינת הסטארט-אפים למרות שרבים בתעשיה, לעניות דעתי – עדיין לא מבינים את זה.

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

בחלק השני השתתפתי בפאנל מקצועי (עם ד\"ר עירית הדר מאונ\' חיפה, עצמון הד-טוב VP R&D בפונטיס, וניר סלע – מנהל פיתוח התוכנה ברפאל) שדן במיומנויות הנדרשות בכדי להיות ארכיטקט, או אולי – ארכיטקט \"מוצלח\". פעם ראשונה שאני משתתף בכזה פאנל – ואני מקווה שהצלחתי לתרום לדיון ולעניין.

מקור

אני אספר בקצרה על שני sessions שהיו לי מעניינים במיוחד:

מחקר על תפקיד הארכיטקט, כיצד הוא נתפס ע\"י אלו שעושים אותו וכו\'
נעשה ע\"י סופיה שרמן, נעמי Unkelos-Shpigel (אני לא רוצה לכתוב את זה לא נכון :)), וד\"ר עירית הדר (שהייתה איתי גם בפאנל).

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

רוב התוצאות שאציג להלהן הן מסקר בו נשאלו מאה ומשהו ארכיטקטים על העבודה שלהם: מה הם עושים מול מה שהם חושבים שהם צריכים לעשות. כל התוצאות הן בסקאלה של 1 עד 5 (כלומר: 3.5 ממוצע) – כאשר המספר המדויק לא ברשותי – אני מספק רק \"קריאה\" שלי מגרף מסוג Bar Chart. למשל:

  • ארכיטקטים הם חלק מצוות הפיתוח (בערך 3.6) בעוד הם חושבים שהם צריכים להיות פחות חלק ממנו (בערך 3.4).
  • ארכיטקטים מובילים את תהליך פיתוח התוכנה (3.1 = נטיה ל\"לא\"), אבל הם מאמינים שהם צריכים לעשות זאת (3.9)
  • הם גם משתתפים במידה רבה בישיבות תכנון (3.9) – אבל מאמינים שצריכים להיות שם הרבה יותר (בערך 4.35).
  • בגדול ארכיטקטים מאמינים שהם צריכים להיות:
    • שותפים משמעותיים יותר בפיתוח המקצועיות ומתודולוגיות הפיתוח בארגון.
    • שותפים משמעותיים יותר בהגדרת המוצר / חקר השוק.
    • שותפים משמעותיים יותר בגיוס עובדים.
  • מה הם חושבים שהם צריכים לעשות פחות?
    • קידוד: עושים לא-הרבה (3.1) – אבל רוצים לעשות פחות (3)
    • אחריות על באגים / תחזוקת-קוד: עושים מעט (2.6) – אבל רוצים לעשות משמעותית פחות (בערך 2.3).
ניתן לקרוא את התוצאות ולומר: \"ברור! כמו כולם הם רוצים להשפיע יותר, לעשות יותר עבודה כיפית – ופחות עבודה משעממת.\". אין לי מדד להשוואה למפתחי תוכנה – אבל אני מניח שהיינו רואים מגמות דומות.

בכל זאת, שימו לב שהשאלה לא הייתה \"מה הייתם רוצים לעשות\", אלא \"מה אתם חושבים שאתם צריכים לעשות (should do)\". אני מעריך (היפותזה) שמפתחים ותיקים לא היו חושבים שהם צריכים \"להנחות את הארכיטקטורה של כל פעילויות התכנון\" (\"Provide architectural guidelines for all software design activities\" – ההדגשה שלי) – עושים: 4.1, חושבים שצריכים: 4.37.

חשבו על הטיעון הלוגי הבא (שרץ כנראה במוחו של כל ארכיטקט תוכנה, בשלב כזה או אחר):
\"אם אני ארכיטקט\" וגם \"למוצר יש ארכיטקטורה\" => \"אני צריך להגדיר את הארכיטקטורה\".

הבעיה:

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

השפה והמושגים בהם אנו משתמשים משפיעים על האמונות שלנו, ואני מאמין שלקרוא לתפקיד של כמה מאנשים על שם פעולה בה עדיף שיהיו כמה שותפים – היא פשוט לא מועילה.
ממש כמו שהשם QA הוא לא מוצלח – כי הוא מסיר אחריות מהמפתחים לאיכות הקוד שלהם. \"אני כותב – אתה אחראי לאיכות…\".

אני חושב שהייתי מעדיף שיקראו לארכיטקט Principal Engineer או System Engineer וכו\'…

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

Wix – להגיע ל n מליון משתמשים (n = כרגע 50 וקצת)
טוב… רציתי לגעת גם במצגת של יואב מ Wix – אבל גם רציתי לכתוב פוסט קצרצר.
אתם יכלים למצוא ביוטיוב את ההרצאה – בגרסה מעט יותר מוקדמת שלה.

כמה נקודות מעניינות שעלו:

  • אם Wix הייתה \"עושה ארכיטקטורה נכונה מהתחלה\" – היא לא הייתה שורדת את השנים הראשונות.
  • רכיבים במערכת צריכים להיכתב לתקופה קצובה – ואז להיכתב מחדש (טיעון מקובל בעולם ה Micro-Services). כתבו קוד שיהיה קל-להחלפה.
  • לא זקוקים לבסיס נתונים NoSQL בכדי לעשות NoSQL. עברתי, בזמנו, חוויה דומה – וכתבתי על כך פוסט.
  • כל מפתח מחזיק הרשאות ל production servers – אנו מעסיקים רק את מי שאנו סומכים עליו (אני מתרגם זאת לעצמי: אנו לא סומכים על אף-אחד, בנינו תשתית מתאוששת-עצמית – ולכן אנו יכולים \"לסמוך על כולם\"). זו גישה דיי מקובלת בעולם ה CD.
  • ההעדפה של Wix ל Managed Data Center (דוגמת Rackspace – אני לא יודע עם מי באמת הם עובדים) על פני AWS – שם כמות המשתנים הבלתי ידועים / נשלטים (למשל: מה החומרה שלי, latency) – היא קטנה יותר.
  • הערה: פרופיל השימוש ב Wix הוא דיי חריג בעולם ה Web (המון המון תוכן סטאטי) – ולכן אני מציע לשקלל עובדה זו בכל רעיונות Scalability שאתם לוקחים מהם.
כמו שאמרתי, Wix היא במיינסטרים של עולם ה Start-up Web ו CD.

סיכום

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

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

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

כיצד מגדירים ארכיטקטורה? צעד אחר צעד

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

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

התוצאה: מסר יותר חד (\"כך כותבים קוד נקי!\"), בד בבד עם אשליה שאנשים באמת כותבים קוד שכזה \"במכה ראשונה\".

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

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

בואו נצא לדרך.

כיצד מתחילים להגדיר ארכיטקטורה?

הרי אפשר להיתקע ולבהות ב IDE (\"אני בוהה בו, הוא בוהה בי\") זמן-מה מבלי להגיע להחלטה כיצד להתחיל.
כאשר יש כיוון – הקוד רץ מעצמו, \"7 המלכות והפרש\" זה לא באמת \"אתגר\" תכנותי עבורנו. הקושי לרוב יהיה לבחור כיוון ראשוני לארכיטקטורה.
דרך אחת היא פשוט מאוד להתחיל ולכתוב בלי סדר – ואז לעצור, לספוג תובנות מהקוד, ולהיעזר בהן בכדי לקבוע את הכיוון ולבצע Refactoring. לא לכולם זה הולך בקלות.
שיטה קטנה אחרת היא כלל שמגיע מתכנון-מונחה עצמים, ו DDD: \"המלים בהן אנו משתמשים לתאר את הבעיה \'חייבות\' לקבל ייצוג מתאים (מחלקות) במערכת\".
אז הנה, קיצרנו לעצמנו מעט – ויש לנו נקודת התחלה כלשהי.
מהם המלים שחוזרות על עצמן בתיאור הבעיה? \"מלכה\" ו\"לוח\" (לפחות אצלי):
להבהיר: במיוחד במערכת כ\"כ קטנה, במיוחד כשכותב אותה אדם בודד, ובמיוחד בעולם האג\'ייל – אין הגיון לייצר תרשימי UML! יצרתי את התרשים רק עבורכם – קוראי הבלוג.
האופן הטבעי לעשות זאת הוא לכתוב קוד מסגרת ראשוני (כלומר: skeleton):

לגבי שפת רובי (הנה פוסט \"נחיתה\" – למי שלא מכיר), attr_accessor היא דרך מהירה לייצר getter/setters לרשימת ה symbols שהוצמדה לפונקציה. למשל, במקרה של המחלקה Board ייווצר משתנה בשם queens@ שערכו nil, מתודה בשם queens שמחזירה את ערך המשתנה, ומתודה בשם =queens שמאפשרת לבצע השמה לערך בעזרת סימן \"=\" (להלן הסבר יותר מפורט)

כבר ביצירת השלד הזה – בעצם לקחתי כבר כמה החלטות \"תכנוניות\" (להזכיר: קוד < תכנון < ארכיטקטורה – אך יש ביניהם דמיון רב).

עם הניסיון אנו יודעים להחליט מהר יותר מה אנו מעדיפים, ואנו יודעים מהר יותר להסביר מדוע. כשהייתי מהנדס תוכנה צעיר – הייתי יכול להשקיע זמן רב בכדי להחליט כיצד אני רוצה לייצג את הדברים הבסיסיים ביותר. 
אלו החלטות כבר \"לקחתי\"?
דילמה: כיצד לייצג את הלוח והכלים עליו
  • אופציה א\': לוח כמטריצה 8×8 של מערך המחזיק אובייקטים.
    • יתרונות: מודל פשוט וקל להבנה
  • אופציה ב\': ניהול רשימות של הכלים השונים – כאשר כל כלי מחזיק את המיקום שלו על הלוח.
    • יתרון: יותר יעיל (טיול על x כלים – שזה מספר קטן מ 64 תאים – בכדי למצוא כלי) – לא משמעותי כאן.
    • יתרון: מישהו יצטרך לחשב \"איום\" של כלי על כלי אחר. זה גורר דילמה אחרת (מייד) – שאת התוצאה שלה, אופציה זו \"תקבל\" בצורה טבעית יותר.
לכן בחרתי – באופציה ב\'. עד כמה ההחלטה קריטית? – לא ממש. אם המפתחים איתם אעבוד יעדיפו את אופציה א\' – אזרום איתם ללא נקיפות מצפון.
דילמה (forward thinking): כיצד מחשבים איומים של כלי אחד על כלים אחרים
קשה להתקדם ביישום פתרון לבעיה – מבלי שהנקודות המפתח ברורות, והנה אחת כזו.
  • אופציה א\': הלוח הוא זה שמחשב איומים בין הכלים
    • יתרונות: אני מעריך שאופציה זו תדרוש פחות שורות קוד.
  • אופציה ב\': כל כלי הוא זה שמחשב את האיומים שלו
    • יתרונות: יותר OO, משום ש\"הצלחנו למצוא\" מומחיות למחלקות. מלכה מומחית בלהכיר את האיומים שהיא מפיקה, ופרש מומחה בלהכיר את האיומים שהוא מפיק.

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

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

למי שלא מכיר UML – חץ משמעו קשר, שמתבטא בפועל ב member של המחלקה. כלומר: חץ דו-כיווני אומר שבמחלקה Queen יש member בשם board@, ובמחלקה Board יש מערך של members בשם queens@.
יש לנו כאן קשר דו-כיווני, מה שאמור להדליק לנו נורה אדומה. ככל האפשר (עקרונות ה OO) – מומלץ להפחית קשרים דו-כיווניים (וקשרים בכלל) מהמערכת.
בואו נזכר מדוע אנו רוצים את הקשרים האלו:
  • מלכה מכירה את הלוח – כי היא צריכה לבדוק מה יש בתאים מסביבה – (\"()board.content_at\").
  • לוח מכיר את המלכות – כי הוא מציב את המלכות.
הממ… האם אפשר \"לפטור\" את עצמנו מאחד מהקשרים? נראה שכן. אפשר שהלוח לא יהיה אחראי להצבה (הנה הגדרת אחריות). נגדיר מחלקה אחרת, BoardManager (שם מקומם משהו) שתיקח ממחלקת ה Board את האחריות ואת התלויות שבאות איתה.
בואו נכסה עוד יכולת נדרשת לתכנון: היכולת להדפיס את הפתרון (כלומר: מצב הלוח בו 7 מלכות ופרש לא מאיימים זה על זה) על המסך. לצורך המערכת הזו – מדובר בהדפסת ASCII ב console.

דילמה: כיצד לבצע את ההדפסה:

  • אופציה א\': ה Board אחראי להדפסה
    • ייתרון: יש למחלקה את כל המידע הדרוש להדפסה
    • חיסרון: עכשיו יהיו למחלקה 2 אחריויות: א. לנהל את ה state של פתרון, ב. לנהל את ההדפסה של ה state הזה. 2 אחריויות היא חריגה מה SRP (קיצור של: Single Responsibility Principle).
  • אופציה ב\': ה Board מדפיס את הלוח, ועושה to_s (כלומר: toString ברובי) למלכה – כדי לתת לה את האחריות כיצד להציג את עצמה (למשל: האות \"Q\" או סמיילי)
    • ייתרון: פיזור אחריות בין אובייקטים שאחראים למשימה (scalability פיתוחי)
    • חיסרון: פיזור אחריויות – אין SRP
  • אופציה ג\': ליצור מחלקה נוספת שאחראית על ההדפסה
    • ייתרון: הפרדת אחריויות ברורה בקוד
    • חיסרון: מה השתגענו – עוד מחלקה? כלומר: יותר קוד לכתוב
הדבר הראשון שיש לשים לב אליו במקרה זה הוא שיש לנו 3 אופציות: אופציה א\' ו ב\' הן יחסית דומות בעוד אופציה ג\' שונה. במקרה שכזה כדאי שתידלק לנו נורה אדומה (קטנה) בשל זיהוי דפוס קבלת החלטות שנקרא \"A, A-, B\".

מה זה אומר? באופן טבעי כבני אדם, אנו נוטים להעדיף השוואות נוחות.
קל יותר להשאוות בין אופציה A לאופציה -A שדומה לה, אך פחות טובה ממנה ולבחור את A, מאשר להחליט בין A ל B.
לכן, אנו נוטים \"לזנוח\" את B ולהתמקד בהשוואה בין 2 האופציות הנוחות בלבד.

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

  1. שלב א\': A מול A-, ולהעלות את אופציה A \"לגמר\".
  2. שלב ב\': לבחון, מחדש, את אופציה A מול אופציה B.
אני חייב להודות שבמקרה שלנו זה סוג של אופציה A1 ו A2 – כי קשה לי לומר בהכרח מי עדיפה מבין אופציה א\' וב\'.
אבל, אם אתם בשלב תכנון ועולות 2 אופציות דומות שאחת נראית בבירור פחות טובה – הסירו אותה. היא נותנת הרגשת ביטחון באופציה הדומה לה – על חשבון אופציות אמיתיות אחרות
אם אתם מעוניינים להעביר החלטה \"בלי שקולגות יפריעו לכם\" (לא יפה!!) – צרו אופציה A- מגוחכת, בכדי להעלות את סיכוייה של אופציה A להיבחר. עוד פרטים על כך אפשר לקרוא בפרק הראשון בספרו של דן אריאלי \"לא רציונלי, ולא במקרה\".
חזרה אלינו:
אנו רוצים לקבל באמת את ההחלטה הטובה ביותר, ולכן הסרתי את אופציה ב\' שנראית לי פחות מאופציה א\'. ולכן:
  • אופציה א\': ה Board אחראי להדפסה
    • ייתרון: יש את כל המידע הדרוש להדפסה
    • חיסרון: עכשיו יהיו לו 2 אחריויות: א. לנהל את ה state של פתרון, ב. לנהל את ההדפסה של ה state הזה. 2 אחריויות היא חריגה מה SRP (קיצור של: Single Responsibility Principle).
  • אופציה ב\' (החדשה): ליצור מחלקה נוספת שאחראית על ההדפסה
    • ייתרון: הפרדת אחריויות ברורה בקוד
    • חיסרון: מה השתגענו – עוד מחלקה? כלומר: יותר קוד לכתוב

בדילמה ביניהן – אני בוחר באופציה ב\'. 
אופציה א\' היא מהירה יותר למימוש, אבל אני אישית מרגיש יותר נוח לתקתק קצת יותר – ולבנות מבנה שהוא יותר scalabale לעתיד.

מצד אחד, זוהי אולי \"שריטה של ארכיטקט\". המערכת שלנו הולכת להיות קטנה למדי (אין לה \"עתיד\") – והקוד יהיה ניתן לניהול גם ללא קנאות ל SRP. זהו Waste.
מצד שני, זהו הרגל טוב למערכות גדולות – והרגלים לא מחליפים בקלות כמו גרביים. אני מעדיף לחיות עם אינטואיציה שהיא פחות יעילה למערכות קטנות, אך יותר יעילה למערכות בינוניות / גדולות (שם אני מבלה את רוב זמני).
אני בוחר באופציה ב\' והמבנה שלנו כעת נראה כך:
ארכיטקטורה א\'
זהו, האם הגעתי לארכיטקטורה המושלמת לבעיה הנתונה?
אז זהו… בוודאי שלא!
  1. אנו לא בעסקי המושלמות (לפחות לא אני). המטרה היא לייצר ארכיטקטורה \"טובה\" (ככה \"top 5\") ולא \"הטובה ביותר\". למה? כי זה פשוט בלתי-אפשרי לוגית. כל דרישה חדשה למוצר הופכת את הסדר בין כמה הארכיטקטורות הטובות ומציבה ארכיטקטורה אחרת בראש הטבלה כ\"ארכיטקטורה הטובה ביותר\". מכיוון שאי אפשר לחזות את העתיד – אז אי אפשר גם להעריך איזו ארכיטקטורה, מבין הארכיטקטורות \"הטובות\" היא הטובה ביותר. פשוט אי אפשר.
  2. ארכיטקטורה מתאימים להקשר מסוים: מי הצוות שיעבוד איתה, מי הלקוח, ומה הסיטואציה (לחץ, פרויקט אסטרטגי, וכו\'). לא יהיה נכון להשוות ארכיטקורות שונות כאשר יש להן הקשרים שונים.
    למשל: בארכיטקטורה הזו אני עומד להיות המפתח. מה שטוב עבורי, לא בהכרח מספיק טוב לכל אדם אחר.
  3. את הארכיטקטורה ניתן באמת להעריך רק לאחר זמן ממושך. דברו איתי עוד חודשיים – ואספר לכם כמה טובה היא הייתה.
    כלומר: מדובר בהימור מחושב.
ברגע שהתגבש מבנה בסיסי, זוהי נקודת זמן טובה להשוות אותו למבנה אחר.
מה קרה? לקחנו סט של בחירות במהלך הדרך – והשוונו בחירה מול בחירה, אך לא תמונה מול תמונה.
ייתכן שלאור הבחירה הראשונה שביצענו – אכן בחרנו בחירות טובות. אבל, אם הבחירה הראשונה הייתה אחרת – היינו מגיעים לסט בחירות שסה\"כ הוא מוצלח יותר.

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

ארכיטקטורה ב\'

היתרונות של ארכיטקטורה א\' הם:

  • הפרדת אחריויות מקיפה (כל מחלקה אחראית על משהו אחד)
  • מוכנות לגדילה* (development scalability). 
* מדוע מוכנות לגדילה היא ייתרון? תמיד הרי נוכל לעשות refactoring למבנה הרצוי, ברגע שנזדקק לו בפועל.
המוכנות לגדילה ייתרון במידה ויש עוד מפתחים שעובדים על הפתרון ולא בקיאים בשיקולים כמו המתכנן. ע\"י בניית \"תשתית רחבה יותר\" המתכנן יכול להכווין את המפתחים הנוספים לכיוון הרצוי.
עבורי אישית זה עניין של הרגל / שלווה פנימית.
היתרונות של ארכיטקטורה ב\' הם:
  • מעט מחלקות – מעט קוד -> יותר מהר לפתח. בד\"כ עוד מחלקות דורשות כתיבה של עוד \"Gluing Code\".
  • הפרדה בין אחריויות – אם כי פחות מקיפה.
אין פה עניין של ארכיטקטורה \"נכונה\". שתי הארכיטקטורות סבירות, ומה שנותר לנו הוא שקלול תמורות (trade-off) – איזו ארכיטקטורה נראית לנו מתאימה יותר בהקשר הנתון. בארכיטקטורה, בוחנים trade-offs כל הזמן.
אני – בוחר בארכיטקטורה הראשונה. אני מרגיש פחות נוח עם מחלקת Board שאחראית גם על ניהול מצב, גם על הדפסה, וגם על הכרות עם הכלים השונים.

סיכום התהליך

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

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

  1. הידע על הארכיטקטורה איננו קיים – הוא מתגלה. אנו מגלים תוך כדי תכנון ומעט קידוד (\"spike solutions\" ו/או POCs) תובנות חדשות, ומשלבים אותן בארכיטקטורה.
    נכון, שאם בנינו מערכות דומות בעבר – אנו יכולים להשתמש בידע שנצבר שם בכדי לקצר תהליכים.
  2. אפשרויות בחירה הן קריטיות לתכנון איכותי. ההרגל נוטה למשוך אותנו ל\"פתרון הראשון שיכול לעבוד\" ולהישאר שם, אבל חשוב לעצור לרגע וליצור עוד אלטרנטיבות ישימות – ואז לבחור מביניהן את הטובה יותר.

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

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

    למשל: בארכיטקטורה א\' הצבנו את כל אחריות ההדפסה על הלוח. בגלל ריבוי הכלים (מלכה, פרש) הוא יאלץ לבצע if (או case) ולהגדיר התנהגויות לכל כלי (המקביל לקריאה ל ()to_s בארכיטקטורה ב\'). מצב כזה נחשב כ bad smell בו מומלץ לעשות refactoring לכיוון State Pattern. מצד שני – המצב שייווצר יפגע ב SRP. פה יש עניין של הבחנה עיקר וטפל – שמבוסס במידה רבה על ניסיון מכיוון שאין דרך מתמטית \"להוכיח\" איזה מצב הוא עדיף. ניסיון הוא בסהכ ה\"מושכל\" ב\"הימור מושכל\", כמובן.

זהו, בחמש דקות – עיקרו של תהליך הגדרת הארכיטקטורה.

סיכום

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

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

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

—-
[א] מלכה ברבים, מלכות, יש לקרוא כמְלָכוֹת (כ\' רפה) – כמו \"MELAHOT\"
[ב] \"8 המלכות\" זו כמובן הבעיה המקרית. אני רציתי לגוון ולנסות משהו טיפה אחר.

מהי התשתית (Framework) הטובה ביותר? [דעה]

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

אני הולך להמליץ לכם על ה Framework הטוב ביותר שנתקלתי בו, באמת!

האם זה Spring Framework?

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

בטח מדובר ב AngularJS – אין דבר חם יותר בשוק היום!

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

המסקנה הברורה היא בטח MeteorJs – מה יכול להיות יותר יעיל מ Angular?!

– רעיון טוב, אבל לא.

מתחכם?

– לא ממש. אין לי כוונה לסיים את הפוסט הזה עם קלישאה (נכונה) כמו: "כל כלי מתאים למשימה אחרת"…

אני רוצה להמליץ לכם באמת על משהו שהוא מבחינתי תובנה. לצורך הדוגמה (נאמר שמפתחים בצד-הלקוח) אציע לכם את התהליך הבא:

  1. לכתוב את המודול הראשון / הפרויקט הראשון ב Backbone.js
  2. במידה ויש הצלחה – לזרוק את Backbone. אם אין הצלחה – להמשיך איתו עוד סיבוב.
  3. לאחר שזרקתם את Backbone – ללמוד AngularJs, MeteorJs או React.js – והתחיל לכתוב בהם.
ברור לי שעל-פניה זוהי עצה מטופשת למדי!

אז מהי התשתית אותה אני מציע?

התשתית היא לא אף אחת מאלו שהוזכרו למעלה.
זוכרים את "הסיפור שאינו נגמר"?
בקצרה: ילד לא-מקובל בשם באסטיאן קורא ספר על גיבור בשם אטריו. אטריו יוצא במסע חיפוש מפרך אחר האחד שיציל את הממלכה בה הוא חי מהכחדה קרובה ע"י "הלא-כלום", הבלתי ניתן-לעצירה.
אטריו חוצה את הממלכה מקצה לקצה, נלחם, נפצע, מאבד חברים, וכמעט מאבד תקווה – רק כדי לגלות שהאחד שהוא מחפש בכלל לא נמצא בממלכה.
"מדוע אם כן נשלחתי למשימה?" הוא שואל ביאוש, לקראת סוף הסיפור, את הקיסרית הילדותית (זה שמה. היא דווקא בוגרת מכולם).
התשובה היא שהמסע אותו עבר אטריו דווקא סייע למצוא את האחד – ולקשור איתו קשר. האחד הוא בעצם באסטיאן, הילד שקרא בספר את מה שבעצם התרחש אצל אטריו. הקריאה בספר הייתה שותפות בחוויות של אטריו, שותפות שיצרה קשרי חברות בין שני הדמויות שלא הכירו זו את זו. החברות יצרה מחויבות, שגרמה לבאסטיאן להסכים ולהסתכן – ולהציל בעצם את הממלכה.
(איזה סיפור נהדר!)
Opaque
Efficient Frameworks
"הסיפור שאינו נגמר" הוא משל מצוין לתהליך אחר שמתרחש, או בעצם – לא מתרחש כ"כ, בעולם התוכנה.
כשאנו בוחרים את התשתית בה ניתן לכתוב את הקוד בצורה המהירה ביותר, אנו בעצם מתמקדים ב efficiency. יותר גרוע: ב efficiency מנקודת מבטו של המתכנת החזק בקבוצה – זה שנותן את ההמלצה על התשתית. "האם אפשר לכתוב פונקציה ב 6 שורות של קוד – ב 2 שורות בלבד?" – היא חשיבה ברורה על efficiency.
בעצם, בתשתית הולכים לכתוב עוד הרבה מאוד מפתחים, והם הולכים להיות זריזים [א] (efficient) אך לא יעילים (effective) – כאשר הם לא מבינים מספיק טוב את הסביבה בה הם עובדים.
שנים אחר שנים, עבדתי עם מפתחים שסיפקו להם "תשתיות עם הגנה בפני-חוסר ידע" – בכדי לדלג על שלב ההבנה של מה שמתרחש סביבם. אנו קוראים לזה Transparency (שקיפות), אך זה בעצם Opaqueness (אטימות) – לא יודעים מה מתרחש בפנים. בורות זו היא נוחה ב 95% מהזמן, והרסנית ב 5% מהזמן שנותר.
אם המערכת וה domain שלכם פשוטים – זה דווקא מסתדר. זה מודל העדר, בו סוללים לפרות מסלול סגור ומוגדר מראש בו עליהן לנוע מתחנה לתחנה. אם ה domain מורכב – חבל לכם על הזמן. הנזקים שיווצרו מחוסר ההבנה הכולל הם קשים, וכמה בעלי המקצוע שכן מבינים מה באמת מתרחש – לא מצליחים לחפות על עדר של פרות (סליחה, על הביטוי הלא נעים) שנע בחוסר-הבנה.
עבדתי בחברה, שהחליטה שהמפתחים שלה (שהוגדרו כעילויים, ברובם) לא מסוגלים לכתוב ב ExtJS – ספרייה לפיתוח צד-לקוח, שכבר בעצמה מחביאה כמעט כל מה שקורה בצד הלקוח במקבץ מרשים של רמות-הפשטה.
מה עשו? כתבו Framework נוסף, מבוסס XML, כדי להחביא עקרונות של ExtJS כמו MVC, ניהול אירועים, ועוד. "רק תגדיר איך המסך נראה ומה קורה כשלוחצים על כפתור" – הייתה שיטת העבודה.
התוצאה – לא הייתה טובה. והמסקנה (הברורה) הייתה שצריך לבנות עוד תשתית בכדי לכוון אפילו יותר את המפתחים. "הם לא מסתדרים".
בחברה אחרת – עושים כך במשך שנים. חלק ניכר מהמפתחים שכח כבר מהי שפת תכנות, והוא יודע רק לעבוד בכלים מגבילים.
חברה אחרת שעם אנשיה נפגשתי, סיפרו איך הם עובדים: צוות-על שמפתח תשתיות למפתחים הרגילים – שמגביל את צעדיהם ומגן עליהם מטעויות. כל פיצ'ר משמעותי מפותח שם – והצוותים הרגילים רק עושים "השלמות". האחריות, כמעט ולא עוברת לכלל המפתחים, וצוות התשתיות נמצא עם "היד על הברז" בכדי לשלוט על מה שהמפתחים האחרים יוכלו לעשות.
Transparent

Effective Frameworks

יש תשתיות אחרות, שמבזות את כל מה שנחשב כזריז (efficient).
כדי לכתוב בהן משהו – יש לכתוב יותר שורות קוד. הן לא מגינות על המפתחים, והן משקפות למפתח את מה שקורה "בפנוכו", בלי שום התחשבות בבלבול הרב שיכול להיווצר מכל הידע הזה.
תשתיות אלו הן תשתיות שהן באמת Transparent – רואים את כל מה שקורה. דוגמאות נפוצות יהיו Node.js (כמובן), Backbone.js (אותה ציינתי קודם), וגם מערכת ההפעלה Linux (בה כ"כ הרבה הוא גלוי למשתמש).הזריזות של ה Frameworks ה Efficient, מפנה את עצמו ללמידה-מעמיקה מואצת של המשתמש. כשכותבים ב Backbone.js מחברים "בידיים" את ה View ל Controller וקושרים "בידיים" את האירועים. זו עבודה – אבל כל מי שכותב ב Backbone.js מבין דיי מהר כיצד פועל ה MVC בעולם של Backbone.

שאנו מגייסים עובדים, אנו מחפשים את "הטוב ביותר", אבל המנגנונים שאנו יוצרים (מכוונה טובה) – שוחקים חלק גדול מהם. פעם אחר פעם ראיתי מתכנתים שמתקבלים לעבודה, אבל לאחר 3-4 שנים, כבר לא מסוגלים לעבור מחדש את אותם ראיונות ומבחנים. התהליך השוחק של עבודה בתשתיות "מגוננות" גורמות למתכנת לאבד את הברק, ולהפוך לחצי-טכנוקרט. וכן, אולי הוא עבד בכמה שנים הללו 15% יותר מהר.

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

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

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

אם כן – מה עדיף?
להיות מהיר, או להיות יעיל?

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

גם חשוב מאוד לספק Delivery ולהתקדם מהר. כמה ארגונים יכולים "לספוג" מפתחים שמפתחים יותר לאט? נכון גם לשאול: כמה ארגונים יכולים "לספוג" מאסה של מפתחים שרק "מסתדרים" ולא ממש מבינים מה הם עושים?

אני מאמין, שהתשתית (Framework) הכי טובה שיש היא התשתית האנושית – מפתחים שמבינים מה הם עושים. אני לא חושב שמישהו יחלוק על כך: כולנו רואים כיצד המפתחים הבולטים יכולים לעשות כל-כך הרבה יותר. אני גם טוען שהתשתית האנושית לא יכולה להירכש רק על ידי גיוסים והדרכות – כפי שמקובל בתעשייה. אני מאמין שהדרך לשם היא לבחור במתכוון ובצורה אסטרטגית בסביבות עבודה בעלות-שקיפות (Transparency) בכל פעם שמתמודדים עם Domain חדש או טכנולוגיה חדשה.
סביבות בעלות פריון גבוה (נאמר: AngularJS או RoR) הן הסביבות המתאימות לאלו שכבר מבינים ועברו את שלבי הלמידה – ולא כתחליף ללמידה משמעותית של הטכנולוגיות, וסביבת-הריצה של המערכת.

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

אם אתם יכולים להתחיל בעבודה בתשתית Transparent בכדי להגביר למידה, ורק אז לעבור תשתית היעילה – עדיף. קודם תבנו את התשתית האנושית של ההבנה – ורק אח"כ תתמקדו בתשתית התוכנה שחוסכת שורות קוד.

הנה הנוסח, שהופיע בתחילת הפוסט:

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

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

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

—-

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

—-

[א] התרגום "זריז" ל Efficient מתאים לי יותר למטרות הפוסט. בלשנים – התמודדו!

דפוסי ארכיטקטורה (Architectural Patterns): דפוסי עיצוב, בגדול

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

יותר גרוע: יש ארכיטקטים שנכנסים לחדר (ואולי זו בכלל דלת עץ זולה שלא נסגרת היטב), וגם הם מאמינים באגדת "הארכיטקטורה האחת" שאותה מוצאים הארכיטקטים מתוך המאגר הסודי של דפוסי הארכיטקטורה. הם מקדישים כמה דקות בכדי לסגוד לדפוס הארכיטקטורה החביב עליהם ואז מותחים את מהות המוצר להיות בדמותו של דפוס הארכיטקטורה שנבחר. אאוץ'!

אתן לכם מטאפורה לדפוסי ארכיטקטורה:

דפוסי ארכיטקטורה הם כמו מתכון למרק

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

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

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

הדפוסים עליהם נעבור בפוסט

  • כדור גדול של בוץ (Big Ball of Mud)
  • "הדר המסודר" (Hadar Amesudar)
  • "שכבות עוגה" (Layered Architecture)
  • MVC (קיצור של Model-View-Controller)
  • "דודות מרכלות" (Event-Driven Architecture)
  •  Command-Query Separation (בקיצור: CQRS או CQS) 
  • "גרעין קטן" (MicroKernel) ודרכו נזכיר גם Plug-in Architecture
  • "מיקרו-שירותים" (Micro-Services Architecture, בקיצור: MSA)

דפוסים נפוצים אחרים שלא נעבור עליהם הם, אך שווה להזכיר:

  • צינורות ומסננים (Pipes & Filters) – אותו כיסיתי בפירוט בפוסט משלו.
  • BlackBoard (רלוונטי בעולם של בינה מלאכותית)
  • MVP, MVVM או Presentation-Abstraction-Control (דומים למדי ל MVC)
  • Broker (רלוונטי במערכות מבוזרות)
  • Model-Driven Architecture (מן "רעיון גדול" שלא ממש "תפס") או Domain-Specific Languages (בקיצור DSL – רעיון שהצליח חלקית).
  • וכו'

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

בגדול אזכיר ש:

  • לדפוסים יש ערך רב כשפה משותפת וכמקור השראה.
  • הצמדות לדפוסים "כפי שהוגדרו" – יכולה להיות הרסנית.
  • כדאי ליצור וריאציות שלכם של הדפוסים שיתאימו לכם יותר, או לשלב רעיונות (Mix and Match) מדפוסים שונים.

בואו נצא לדרך!

כדור גדול של בוץ (Big Ball of Mud)

השם ה(לא) מחמיא הזה כמובן ניתן ל Anti-Pattern. כדור גדול של בוץ הוא מצב בו כל המחלקות במערכת שלנו נמצאות ב Pool אחד משותף, ללא חלוקה ברורה ומסודרת.
מצב זה אולי נוח כאשר יש 10 מחלקות, אך מאוד לא נוח כאשר יש כ 1000 מחלקות במערכת.
אפשר בהחלט להתחיל במערכת עם "כדור גדול של בוץ", לגלות את התכונות והמבנה והרצוי של המערכת – ואז לחלק. ככלל אצבע הייתי מצביע על כמה עשרות מחלקות, לכל היותר, כנקודה בה כדאי מאוד שהיה כבר מבנה מוגדר.

מדוע כדאי לנו לחלק את המערכת לחלקים? בגלל:

  • בעיית ההתמצאות (Orientation)
  • בעיית ה Development Scalability
  • יצירת מסגרת מנטלית של סדר וחוקיות במערכת. זו לא בדיחה: כמה כללים פשוטים מה "נכון" ומה "לא נכון" עוזרים לאנשים בפרויקט להרגיש בטוחים יותר במה שהם עושים. אי קיום הכללים הפשוטים הללו / חוסר סדר בסיסי כלשהו בפרויקט – משפיע על אנשים רבים (לא כולם) בצורה שלילית וברורה
    קשה להבין את ההשפעה עד שלא רואים את המעבר של פרויקט ממצב של "אי-סדר", לאפילו מצב של "סדר בסיסי".
את 2 הבעיות הראשונות תיארתי בפירוט בפוסט מדוע אנו זקוקים ל Software Design?

"הדר המסודר"

אוקיי: הבנו שיש חשיבות לסדר בסיסי במערכת. בואו ניקח בחור מסודר, שיקבע סדר.
האם משנה בדיוק מהו הסדר?

הדר בוחר בסדר פשוט:
נשרטט קווים, ונחלק את המערכת ל3 אזורים:

  • מחלקות עד 1000 שורות קוד
  • מחלקות מתחת ל 1000 שורות קוד
  • קבועים, enums, או data objects (שהם בד"כ מאוד קטנים)

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

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

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

  • דואר נכנס
  • דואר יוצא
  • אנשי קשר
  • קונפיגורציה
  • אחר

החלוקה הזו נשמעת יותר הגיונית, אבל גם בה יש בעיות עקרוניות. למשל: הקבוצה "אחר" יכולה להכיל לא-מעט מחלקות שלא מתקשרות ישירות לפונקציה "עסקית" (persistence, helpers, פקדי UI שבשימוש חוזר, וכו'). יצרנו שם "כדור בינוני של בוץ" בפני עצמו. ההתמצאות עצמה היא בעיקר ברמת האזורים ("שכונות בעיר") אך לא מכסה רזולוציה קטנה יותר ("רחובות").
דבר נוסף הוא שכדי להשיג Developer Scalability (היכולת של הרבה אנשים לעבוד על המערכת במקביל) אנו נרצה להטיל מגבלות על היכולת של החלקים השונים של המערכת לתקשר זה עם זה.

מדוע אנו מעוניינים בסדר וניהול תלויות?

נמשיל את העניין לבעיה פשוטה מאוד: הפרדה של קוד בין 2 מחלקות:

את קוד ה Parser אנו מחלקים ל2 חלקים. מדוע?

  • כי יותר קל לנהל קוד בחתיכות קטנות ופשוטות, מחתיכה גדולה ומורכבת יותר.
  • כי אז ניתן לבצע שינוי באזור אחד, עם סבירות גבוהה* שלא יהיה צורך לשנות אזור אחר. פחות בדיקות רגרסיה, פחות "פחד" לשבור דברים, והיכולת של 2 מפתחים לעבוד במקביל בצורה יותר יעילה.
* כל עוד החלוקה היא אפקטיבית
לא מספיק שחילקנו את ה Parser ל 2 מחלקות, אנו רוצים להטיל מגבלות על התלויות ביניהן:
  • הכמסה (encapsulation) – מגבלה מאוד מקובלת בעולם ה OO, שמפרידה בין חלקים ציבוריים (בהם ניתן ליצור תלות) ופרטיים (בהם לא ניתן ליצור תלות). עצם כך שצמצמנו את התלויות – הגברנו את הסיכוי ששינוי בקוד (באזור הפרטי) לא ידרוש שינויים או ישפיע על מחלקות שתלויות במחלקה זו – שוב: Developer Scalability, תחזוקה קל היותר, ויכולת לבצע שינויים בצורה פשוטה יחסית (באזורים הפרטיים).
  • ניהול תלויות (dependency management) – מגבלה שאומרת שמחלקת ה Document Parser רשאית לקרוא ל Paragraph Parse – אך לא להיפך. צמצמנו כך את התלויות עוד יותר.

אציין שבניהול תלויות המטרה היא לצמצם תלויות, לא רק "לנהל/לעקוב" אחריהן.

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

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

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

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

Layered Architecture

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

כללי החלוקה של דפוס השכבות הוא כדלהלן:

  • נפריד את מאגר המחלקות במערכת שלנו לכמה שכבות: שכבות עליונות לטיפול בעניינים ברמת הפשטה גבוהה, כאשר רמת ההפשטה יורדת ככל שיורדים בשכבות.
  • לוגיקה בשכבה גבוהה (n) תהיה מורכבת מהפעלה של לוגיקות (או פונקציות) בשכבה שמתחתיה (n-1). כמו כן, לוגיקה בשכבה גבוהה (n) תעזור להרכיב את הפונקציות בשכבה הגבוהה ממנה (n+1).
  • בד"כ השכבה הגבוהה ביותר היא זו שמתקשרת עם העולם החיצון: משתמש או מערכות אחרות.
  • החלוקה של המחלקות לשכבות היא שלב אחד, ובד"כ יש חלוקות נוספות בתוך השכבה למודולים / תתי-מערכות (sub-systems).
  • בד"כ בכל שכבה יש תת-שכבת API (או "Facade") מנוהלת – בה, ורק בה השכבה העליונה יכולה להשתמש. לא נרצה שכל ה APIs שזמינים בתוך השכבה יהיו זמינים לשכבה מעל – אחרת, איזו משמעות יש להפרדה שהגדרנו?!

בהצצה לתוך השכבה הבודדת, המבנה עשוי להראות דומה למבנה הבא:

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

  • UI – ממשק המשתמש
  • Business Logic – הפונקציה העיקרית שהמערכת ממלאה (ניהול ספקים, רכש, מלאי, וכו')
  • Data Access – בשכבה זו פעם היה הרבה קוד מתוחכם, אך היא הוחלפה חלקית ע"י מערכות ORM – ומה שנשאר בה הם האובייקטים של המערכת שמתארים את הנתונים (Entities, Collections, וכו').
  • Persistency – שכבה זו מתייחסת לבסיס הנתונים (בהנחה שיש כזה, בד"כ רלציוני) – סקריפטים ליצירת הטבלאות (DDL) ולעתים גם קוד שרץ בתוך בסיס הנתונים (stored procedures) [ב].

לחלוקה זו יש וריאציות שונות (הוספת שכבת Service מעל ה Business Logic, או שכבת Presentation Logic מתחת ל UI, וכו') – אבל סה"כ היא מאוד מקובלת.

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

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

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

דפוס ארכיטקטוני שעשוי להתאים במקרים בהם כל פיצ'ר / פיתוח קטן נוגע בכל (או רוב) השכבות הוא דפוס ה microservices – שזוכה לפופולריות רבה לאחרונה. האם גם אותו אפשר "לפספס"? – כמובן!

אציין, שבמערכות Enterprise כדוגמת ERP של סאפ – יש ערך ממשי בחלוקה הנ"ך לשכבות: מערכות אלו מבוססות במידה רבה על מידול (modeling) של נתונים, ואז באמת יש פיתוחים ב Business Layer שלא צריכים לעדכן את ה UI (כי הוא נבנה מתוך מודל הנתונים שלא השתנה), ולא צריכים לעדכן כמעט את הטבלאות בבסיס הנתונים. שכבת ה Business Layer היא משמעותית ומשתנה תדיר – ויש ערך רב בהפרדה זו. כמו כן ניתן לשנות את ה UI (כיצד מרנדרים את המודל) – מבלי לשנות את שכבת ה Business Logic בכלל, וכו'.

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

"הב לי עלים ירוקים…"

Model-View-Controller

בתיאוריה, MVC הוא דפוס נהדר לחלוקה של שכבת ה UI.
תאוריה בצד. בפועל: יש לו בעיה מהותית – שמונעת ממנו לעתים "להמריא".

דפוס MVC בא לחלק אפליקציית UI לחלקים מוגדרים היטב – ולהגדיר תלויות ביניהן.
המקור של MVC הוא ב framework של שפת Smalltalk-80 משנת 1980 בשם "Model-View-Controller Editor".

ה Framework הזה היה מיועד לאפליקציות command line (שיא ה UI באותה תקופה), והחלוקה הייתה בין:

  • Model – הנתונים של האפליקציה / מבני נתונים
  • View – תצוגת הפלט, מה לרשום על המסך
  • Control – קבל ה Input מהמשתמש וטיפול בקלט הזה – בעצם ה Business Logic.
MVC "קלאסי" / "המקורי"

כל מחלקה (או פונקציה) נופלת באחד מהאזורים – וחייבת לציית לכליל הנראות. בת'כלס ה Controller רק יוצר את ה View ולא משתמש בו אח"כ, ושניהם מכירים את המודל. בין מחלקה של View למחלקה של Controller יש בד"כ קשר של 1:1 – והם מתארים "מסך" או "תפריט" באפליקציה.

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

אם כולם היו מכירים את MVC ומשתמשים בו באותו האופן – לדפוס זה היה ערך רב יותר. אולם, יש 2 בעיות:

  • MVC המקורי לא כ"כ מתאים לאפליקציות Desktop, Web או כל מה שאינו command line.
  • יש המון וריאציות של MVC: חלקן קיבלו שמות נבדלים (יחסית) כמו MVP, MVVM או PAC – אבל רוב הווריאציות פשוט משתמש בשם "MVC", תוך כדי ביצוע שינויים במודל "המקורי".
התוצאה המידית היא בלבול וחוסר תקשורת –  FUD. אם דיברנו על כך שדפוסים משמשים כשפה משותפת, אזי MVC משמש לרוב כאנטי-שפה-משותפת: מתכנת אחד אומר "Controller" והשני שומע "Kotroller" (כלומר: מה שהוא מכיר כ Controller). שני המתכנתים רבים כמה ימים, אולי שבועות – עד שהם מצליחים להסכים שבעצם השני לא אידיוט/מפגר/לא מבין – שניהם פשוט מכירים שתי וריאציות שונות של MVC.
בפועל, אוותר על הניסיון לתאר ולאפיין את כל הווריאציות התאורטיות של MVC (להזכיר: יש כאלו שהם Client-Side, כאלו שהם MVC צד שרת, כאלו שהם מערבים צד-לקוח וצד-שרת, וכאלו שמתאימים לאפליקציות Desktop או Native Mobile).
אני ממליץ לוותר על ההגדרות, ופשוט להיצמד לספרייה הרלוונטית שבה אתם משתמשים – והכללים שהיא מכתיבה: בסוף, התועלת העיקרית היא מכללים ברורים שכולם מצייתים להם – קצת פחות אם כלל מסוים מנוסח בווריאציה 2א' או בווריאציה 3ג'.
יש ספריות יש רבות הממשות (וריאציה של) דפוס זה: Struts, Play!, Grails, Ruby on Rails, Django, ASP.NET MVC , Angular.js, Backbone.js ועוד….
מבנה של MVC צד-לקוח מודרני טיפוסי אפשרי
העיקרון המשמעותי ביותר, והמוסכם ביותר בקרב ספריות "MVC" מודרניות הוא ההפרדה בין UI (כיצד משהו נראה על המסך) ל Presentation Logic (כיצד להחליט מה יש להציג על המסך). למשל (דוגמה סופר-פשוטה):
במקום לקבוע כלל ש:
  • אדם עם +500 חברים מקבל icon מיוחד.
מחלקים את המימוש ל2 אזורים:
  • Presentation Logic – אדם עם 500+ מתויג כ "אדם פופולרי במיוחד" (What)
  • UI – אדם המתויג כ "אדם פופולרי במיוחד" מקבל icon מיוחד.
הפרדה זו יוצרת מעט overhead על הפיתוח, אך היא מאפשרת להחליף את ה presentation (כלומר: אלמנטים ב UI / כל ה UI) בצורה קלה יחסית, מבלי "לאבד" או לשנות את ה Presentation Logic שהוא יותר יציב, או לחלופין להוסיף presentation נוסף (נאמר: mobile version), מבלי לשכפל את הקוד לגמרי.
החלפת / רענון שכבת ה UI – היא פעולה תדירה מאוד במערכות ווב. ארגונים גדולים עושים זאת כל 2-3 שנים, ואתרים קטנים מבצעים שינויים לפעמים על בסיס חודשי.

סיכום

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

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

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

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

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

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

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

לינקים מעניינים

הדוד בוב בביקורת עוקצנית על MVC (וידאו): "MVC is not an Architecture"