top of page
  • תמונת הסופר/תJonathan Barda & Niv Sluzki

עלילות הקוד הרקוב והמודל המנטלי



הסיפור על הסטארטאפ הקטן שמנסה לרוץ כמה שיותר מהר קדימה כדי לתת ערך ללקוח, לא חדש.

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

זה הסיפור שלנו.


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


אבל אז הגיעה הבעיה.

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


הסיפור שלנו לא חדש. אז למה בכל זאת אנחנו כותבים אותו?

במילים אחרות: לכו תקראו איזה פוסט של ה-Engineering של Netflix או Spotify או Amazon (״הידד! איך הכל עובד לנו מושלם!״). ואנחנו ניקח את הבלוג, נאסוף את פיסות הקוד ונחזור לנתניה (חדרה / ראש העין / יו ניים איט).


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

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


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

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



איפה היינו ומה עשינו?


ה-Backend שלנו.

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

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



קבלו דוגמא ל-Get Files Endpoint:

**ממומש עם Flask וSqlalchemy





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





עולם של בעיות אני אתן לך


אז אילו בעיות אפשר לראות כאן?


בעיה 1 - יצרנו Coupling בין המודל של הטבלאות ב-DB, לבין התשובה שאנחנו מחזירים ב-API.

כלומר, ה-API שלנו צריך להיות מאוד מודע למה שקורה ב-DB והוא צריך להכיר איך אנחנו מחזיקים את ה-data. כל מיגרציה שנעשה משפיעה ישירות על החלק במערכת שאמור לשרת את ה-UI שלנו. אם נרצה לשנות את הטבלה User, כמעט בטוח שנצטרך לשנות את התשובה ב-API. ואנחנו לא אוהבים כשאנשי ה-Front End מתחילים להתלונן.

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


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

זה קשה לתקשר לשאר אנשי הארגון למה מאוד קשה לייצר פיצ'ר חדש.

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


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


בעיה 4 - לפי כמות ה-Joinים שאנחנו עושים, אפשר להסיק שיש חוסר התאמה בין מודל הכתיבה למודל הקריאה. כתבנו את המידע בצורה שהייתה נוחה לנו, אבל עכשיו כשאנחנו צריכים להחזיר אותו ב-API, אנחנו צריכים לבצע המון שליפות וחישובים מורכבים שיוצרים בעיות Performance רציניות ככל שה-DB שלנו גדל.


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


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



ריבונו של עולם, איך הגענו למצב הזה?


תראו, זה תלוי באיזה יום תתפסו אותנו. כנראה שאם תשאלו אותנו למה הקוד הזה נראה כמו שהוא נראה, ישר נאשים את מתיאס, הג׳וניור שלנו שחירב לנו את כל ה-code base.


Damn you, Mathias!


אבל האמת העצובה היא, שזה קוד שאנחנו כתבנו.


כשאתה סטארטאפ קטן אחרי Seed, כשעוד אין מוצר אמיתי ואין מערכת, אתה לא תמיד יודע לאן אתה הולך.

ואז אתה מאלתר.


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

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

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


וזה לא רק זה.

לא עבדנו בצוותים, ולא היה ownership על אף חלק בקוד.

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

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


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



הקוד הזה רקוב


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

קוד גרוע ששוכב הרבה זמן מתחיל להירקב ומעלה ריח רע.

זה מדעי.


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


זה משפט חשוב אז בואו ננסה להסביר אותו:

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


לדוגמא, נניח שאנחנו מפתחים אתר קניות. באתר שלנו יש:

  1. סל קניות

  2. מוצרים

  3. הנחות

כולם חלק מהמודל הזה, ולא רק הם.

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


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

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

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


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

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

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

וזה כבר ממש מסריח.


קוד שמתיאס כתב באותו הרגע קל לו להסביר, לשנות ולהתאים - המודל המנטלי שלו מתאים לאיך שהוא תפס את הקוד. אבל מתיאס עזב (התפנה תפקיד VP R&D ב-Tifsuni, אתם יודעים איך השוק היום…), הדרישות השתנו והמודל נרקב.




זה כבר לא מתיאס, זה מטיאס


אז מה לא תואם?


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

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

  2. הטמענו את כל הלוגיקה שלנו תחת Flask. ואם בא לנו להשתמש בלוגיקה (=הדומיין) הזו במקום אחר נצטרך להתחיל לחלץ אותה מ-הFramework.

  3. מודל הקריאה ומודל הכתיבה לא זהים! החוקים שמנהלים את הכתיבה למודל, כנראה צריכים מודל שונה מזה המשמש לקריאה מהירה.


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

SOLID, KISS, DRY, Design Patterns, DDD, Coupling, Cohesion, Shleem.



Shleem - the best design architecture


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

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



איך הבנו שזה הזמן להתחיל לשלם את החוב?


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


בנוסף, דברים התחילו להישבר, והתחיל להיות קשה להוסיף טסטים.

הבנו שכמעט בלתי אפשרי לבדוק את הלוגיקה של המודל שלנו בלי ה-DB למשל.

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



סיכום


אז מה היה לנו כאן?

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


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


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


אם הגעתם עד לכאן, אולי אתם חושבים שהתבלבלנו:

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


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

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


אנחנו רצים למסקנות


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



__init__

bottom of page