ליאור בר-און     לפני 3 שנים     כ- 16 דקות קריאה  

Design by Example II

תוכנה

אני רוצה להמשיך ולדבר על בעיות Design לדוגמה, בתקווה שהן מעניינות מספיק. הבעיה שבחרתי הפעם: Rate Limiter.

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

  • היכן יישב המנגנון של ה Rate Limiting? בתוך המערכת – או כרכיב נוסף?
  • איך המנגנון יעבוד בפועל? איך יוודא שה Rate נשמר?
  • האם אנחנו צריכים כלים נוספים (Queue, Database, וכו') – על מנת לספק את המנגנון?

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

מה שאנו נוטים לשכוח…

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

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


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

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

מה דעתכם? איזה פתרון אתם מעדיפים? מה נראה לכם הכי הגיוני?

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

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

  • קביעת השכירות כפרמטר של מספר העובדים של כל שוכר – וכך צמצום מספר העובדים בבניין. כלומר: יהיה משתלם יותר לחברות עם פחות עובדים / פחות צפיפות לשכור. זה עשוי להשפיע גם על תחזוקה / חניה / עלויות אחרות.
  • יציאה בקמפיין שמבליט את המגניבות בלעבוד ב"מגדל ברונטוזרוס" – כך שהתורים במעליות יתקבלו ביתר הבנה: "זו זכות לעבוד בכזה מגדל, תראה כמה אנשים רוצים את זה".
  • גיוון בתחומי העיסוק של השוכרים בבניין. אולי כל אנשי הפיננסים מתחילים לעבוד בין 7:00 ל 7:30, אבל ההגעה לחברות הייטק – עשויה להתפזר על טווח רחב יותר של שעות (8:00 עד 10:00) ולהוריד את העומס?
  • למכור את הבניין, ולהתמקד במומחיות של החברה – בנייה ושיווק נדל"ן (ולא ניהול נכסים).

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

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

חזרה ל Rate Limiter

אוקי. אנחנו רוצים להבין טוב יותר את הבעיה (או הבעיות) שבגינן אנחנו רוצים Rate Limiter ל API שלנו.

השאלה החשובה היא השאלה העסקית: מי רוצה את ה Rate Limiter (או כל Design שאנחנו עובדים עליו),ולמה? איך אנחנו יכולים לבנות ביטחון שאנחנו פותרים את הבעיה הנכונה?

  • בווריאציה אחת נתכנן Rate Limiter שנועד להגן על המערכת שלנו בפני עומס. צד שלישי קורא למערכת שלנו בקצב גבוה (נאמר: אלפי פעמים בשעה), ו/או כל קריאה של הצד השלישי דורשת מהמערכת שלנו כמות נכבדת של עבודה – כך שמעבר לקצב מסוים, המערכת שלנו תגיב לאט מדי לכלל השימושים ואולי תקרוס.
    • להזכיר, גם כאשר יש auto-scaling – הוא לא מיידי. Peak גדול שמגיע ברגע אחד, יכול לגרום לאי יכולת לספק את הבקשות לזמן מה עד שהמערכת גדלה מספיק.
    • נניח שהצד השלישי הוא חלק משני בביזנס. כלומר: אנו מעדיפים לא לתת לו שירות על מנת להגן על המערכת / הביזנס העיקרי.
  • בווריאציה השנייה, אנו רוצים להגן על הלקוחות שלנו מפני עלויות-יתר. הם משלמים לנו על כל קריאת API אבל מצפים שנעזור להם לנהל את העלויות. אם לקוח לא רוצה להוציא יותר מ $5000 בשבוע – אנו נאפשר לו את זה ע"י דחיית בקשות שחורגות מהתקציב.

איטרציה ראשונה: Rate Limiter שמגן על המערכת

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


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

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

האם אתם מזהים כבר את הכשל הלוגי? הנה כמה:

  • נניח שהמערכת יכולה לעמוד בעומס של 60,000 בשעה. הוספה של לקוח נוסף תאפשר 100,000 בקשות בשעה.
  • כמה מהעומס מגיע מפנים המערכת? אולי בעצם השימוש העיקרי של המערכת כבר משתמש ב 20,000-30,000 בקשות בשעה? ואז – 50,000 בקשות נוספות כבר יביאו לקריסה.
  • כמה המספר הזה יציב לאורך זמן? מי יידע לעדכן אותו כאשר שינוי במערכת משנה את הקיבולת הזמינה (להעלות או להוריד)?
  • הכי גרוע אולי, אם מגיע לקוח שעושה מיליון בקשות בשעה – כמה זמן ייקח עד שייחסם?
    • 60,000 בקשות בשעה הן 1,000 בדקה, ו 16 בשנייה. מיליון בקשות בשעה, בממוצע, הן 270 בשנייה – הרבה יותר ממה שהמערכת יכולה לשאת.
    • מה אם מותר לעשות 50,000 בשעה – אבל לקוח בוחר לעשות את כל הבקשות שלו במשך שלוש דקות? התוצאה היא זהה.

זה אגב מה שקרה לנו אז: שעתיים אחרי ש API נפתח לצד השלישי – כל המערכת קרסה. ה Rate Limiter, מומש ונבדק – אבל לא עשה את העבודה. הצד השלישי ידע שלאחר 50,000 קריאות בשעה הוא ייחסם, אבל לא דאג להגבלות מצדו (הוא היה במוד ניסוי). מכיוון שהפנה בקשות בקצב גבוה מאוד, המערכת קרסה תוך דקות, "רק" אחרי כ 10,000 בקשות.

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


בואו נתחיל לחשוב על התכנון. הנה כמה שאלות שהעלתי בתחילת הפוסט:

  • היכן יישב המנגנון של ה Rate Limiting? בתוך המערכת – או כרכיב נוסף? – בהבנת הבעיה נראה לי שאני רוצה את ה Rate Limiting כרכיב נוסף. הוא אמור להגן על המערכת, ושייפול אם צריך – ויגן על המערכת.
    • יתרון נוסף – אוכל להשתמש ב Rate Limiter גם עבור שרתים (מיקרו-שירותים) אחרים. יש לי כלי לשימוש חוזר.
    • חסרון – עלויות. ישנם מקרים ספציפיים בהם זה שיקול מספיק לחבר את ה rate limiter לקוד השרת – אבל אנחנו לא שם.
  • איך המנגנון יעבוד בפועל? איך יוודא שה Rate נשמר?
    • דבר ראשון שברור שהרזולוציה צריכה להיות קטנה. תלוי מאוד אופי הבקשות, אורכן, תדירותן וכו' – אך כברירת מחדל אני אלך על היחידה הבטוחה והפשוטה: שנייה. הגבלת קריאות בשנייה.
    • מכיוון שמה שחשוב לי הוא להגן על המערכת בפני קריסה – חשוב לי שהיה מונה (counter) מרכזי שידחה בקשות בכלל. בנוסף נניח שאנחנו רוצים גם מונה לכל לקוח – לייצר שוויון מסוים. נניח: 1,000 קריאות בשנייה בכלל, ו 200 לכל לקוח. נניח שיש כ 20-30 לקוחות. המספרים המדויקים פחות חשובים לתכנון, אבל קל יותר לדבר ולהתייחס למספר נתון.
      • המקסימום למונה הכללי צריך לנבוע מתוך יכולות המערכת. מספר שנקבע יהיה כלל אצבע שלא יתחשב בשינויים במערכת. לכן, היה עדיף למשל למדוד את ה CPU של השרת הפנימי ולהחליט לפיו (או בעצם: ממוצע CPU כי כנראה יש כמה instances וגם המספר שלהם עשוי להשתנות דינאמית). בכל מקרה, אנחנו באיטרציה ראשונה – ולא נכון לצלול לזה כרגע. נרשום את המחשבה בצד.
    • "האלגוריתם" עצמו לספירה, נשמע לי דיי פשוט ואתאר אותו מיד בהמשך.
  • האם אנחנו צריכים כלים נוספים (Queue, Database, וכו') – על מנת לספק את המנגנון? האמת שתחושת הבטן שלי אומרת Redis (מנוע מבני-נתונים / אולי בסיס נתונים K/V – מבוסס זיכרון. כלי שכדאי להכיר). אני מניח על קצב גבוה כלשהו ובכל מקרה ברור לי שה Rate Limtier צריך כמה מופעים עבור High Availability (לא נרצה שנפילה שלו תפיל את השירות לצד-שלישי) – ורדיס הוא רכיב מרכזי.
    • חשוב בדזיין לפשט, ואולי אנחנו לא זקוקים לרדיס (בקצבים לא גבוהים, גם בסיס נתונים רגיל יכול לעשות את העבודה). תחושת הבטן שלי אומרת לי שכנראה שנרצה, וקל לי אישית לחשוב על הפתרון כך – ולכן אני מתחיל איתו. בסבבים מאוחרים יותר, אולי אמצא דרך לחתוך אותו החוצה, אבל אני הולך כרגע עם מה שהכי פשוט לי לעבוד איתו – ולהתקדם לנושאים נוספים.

הנה תרשים:

  • בקשה מתקבלת ע"י ה Rate Limiter ואני בודק את המונה המרכזי (global counter). אם חרגתי ממנו – אני יכול ישר לקפוץ ל 4b ולדחות את הבקשה.
  • את המונים אני יכול לתחזק ב Redis כ key/value, כאשר ה key הוא שנייה ב Epoch (עבור המונה הגלובאלי) או שילוב של לקוח (שאני מזהה ע"פ ה auth token) + שנייה ב Epoch.
    • ל Keys אקבע expiration של 10 שניות, למשל – כדי שהזיכרון של רדיס לא יתמלא.
    • הנחת יסוד שלי הוא שהשרתים מסונכרנים בשעונים. זו הנחה סבירה לחלוטין בתשתיות הענן הקיימות, ובאמזון, אם זכור לי נכון – אפשר להניח על סטייה מירבית של 1 ms לשרתים באותו Region – מה שלא בעיה עבורנו.
      • אפשר גם לקבוע cloudWatch alert על חריגה בשעונים של שרתים. לא יודע אם הייתי זוכר את זה בסשן Design ו/או ראיון – בטח לא היה לי זמן לחפש בגוגל ולאמת.
    • מי שמכיר היטב את נושא הזמן בטח חושב שיש כאן חור (קטן) בתכנון בגלל Leap Seconds. אני יכול להרגיע ש Epoch מתעלם מ leap seconds – ובכל מקרה זה גליץ' מספיק קטן ונדיר להתעלם ממנו.

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

  • ההחלטה השרירותית כמעט לחסום לקוח יחיד ב 200 קריאות לשנייה אולי בעייתית. תלוי בביזנס – אולי לקוחות פונים מעט וב bursts – ואז חשוב לתת להם את מלוא הקיבולת האפשרית (1000 קריאות בשנייה?). בכל מקרה, מקום לשיפור האלגוריתם ובכל מקרה לא נשמע לי showstopper.
  • עלויות של חומרה: עוד 2-3 שרתים + Redis זה עלות לא מבוטלת. האם היא נדרשת?
    • צעד מידי ופשוט לצמצום עלויות יהיה לממש את ה Rate Limiter על גבי AWS Lambda (או מקבילה בענן אחר). נשמע כמו צעד מתבקש.
    • הנה שיפרתי משהו משמעותי מתוך ביקורת עצמית מובנית 😀
  • ה Scale דיי טוב. ככלל אצבע Redis יכול לטפל ב 100,000 בקשות בשנייה. אפשר לנהל דיון על scales אדירים – אבל נשאיר את זה בצד, עד שיוכח אחרת. במקרה הכי גרוע ה Rate Limiter "קורס" – אך המערכת עדיין מוגנת.
  • עקרונות תוכנה: SOLID / other practices:
    • No single point of failure – לרדיס אין redundancy, אם הוא ייפול (ויעבור מיד restart) – יהיו 10-30 שניות של downtime (הנחה) ומידע שנעלם מהזיכרון בכל מקרה יהיה כבר לא רלוונטי (מונים של שניות קודמות).
  • ייתכן ובאמת החסימה של תעבורה לכל לקוח היא מעבר למינימום ההתחלתי ויכולנו להתחיל עם מונה מרכזי וזהו. תודה לטל רום על ההערה.

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

איטרציה ראשונה: Rate Limiter לניהול עלויות של לקוחות

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

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

  • היכן יישב המנגנון של ה Rate Limiting? בתוך המערכת – או כרכיב נוסף? – במקרה הזה נשמע שעדיף לשבת בתוך המערכת. מדוע?
    • אולי יש מספר קריאות API שמפעילים את אותה פונקציונליות שאנו רוצים לגבות עליה. מנגנון הגבייה/מעקב לא יצטרך לעקוב אחרי הדרכים השונות להגיע ללב הלוגיקה (כלומר: API1,2,3,4 דורשים חיוב)
    • אם יש תקלה ולא הצלחנו לסיים את הפעולה (Exception) – כנראה שלא נכון לחייב. כלומר – אם החיוב היה ב gateway/proxy כמו במקרה הקודם, היה עליו לפעול רק בעת מתן תשובה תקנית.
    • פחות רכיבים במערכת. ייתכן ויש לנו מערכת גבייה ("commission service" או איך שלא ייקרא) – ואז הגיוני לשים את ה Rate Limiter שם. זה עדיין בתוך המערכת – רק במקום הנכון יותר.
  • איך המנגנון יעבוד בפועל? איך יוודא שה Rate נשמר?
    • רדיס כבר לא טוב לנו. מדובר בגבייה של כסף שזה עניין מדויק שאסורים בו פספוסים, ורדיס ש"נפל" ואיבד את כל המונים שלנו – גם אינו מצב סביר.
      • סביר יותר להתחיל בסיס נתוני (רלציוני, או מה שמהערכת עובדת בה) ומשם להמשיך.
    • המנגנון עשוי להיות דיי דומה, נניח Key/Value כאשר המפתח הוא יום Epoch + זיהוי הלקוח. כל זאת כל עוד איפוס המונה ב UTC הוא הגיוני. ייתכן ולא – ואולי יש צורך לנהל את ה offset של אזור הזמן לכל לקוח / או מדיניות אחרת כלשהי.
    • מה עם לקוח שלא השתמש בחיוב שלו במשך כ 3 ימים – וביום הרביעי יש לו חיוב של 125%? האם אנחנו רוצים לאזן מקרים כאלו איכשהו – או להישאר במסגרת נוקשה של יום?
      • בגלל שאנחנו באיטרציה ראשונה – נתחיל ב"מסגרת" נוקשה של יום ונדון בהרחבות אפשריות רק בהמשך.
  • האם אנחנו צריכים כלים נוספים (Queue, Database, וכו') – על מנת לספק את המנגנון?
    • הזכרנו כבר בסיס נתונים, כי מידע צריך להשתמש persistent. אני לא רואה יותר מזה כרגע.

הנה תרשים שמתאר את המבנה:

  • אני מדמיין מבנה קיים כלשהו (בשחור), התוספות הן בכחול.
  • קודם כל, לפני הפעלת פעולה עסקית יש לבדוק האם ניתן לחייב (שלב 1). הוא יגיע ל Rate Limiter והוא מצדו יבדוק את המונה בבסיס הנתונים. הסכמה יכולה להיות key/value פשוט.
  • רק כאשר הפעולה הסתיימה בהצלחה (אני מניח? – תלוי איך המערכת עובדת), מבצעים פעולת charge שאותה נוסיף לחיוב ה counter.
    • אפשר לשאול: "מה יקרה אם יהיה exception ב increase counter? זו לא פעולה אטומית?" – התשובה היא שזה כנראה באג והתשובה כנראה מאוד תלויה במערכת הספציפית ואיך היא עובדת. נשמע לי לא רציני "לדמיין" כאלו פרטים בלי שניתן לבדוק. באופן כללי מכיוון שזה אותו בסיס נתונים – ניתן לצרף טרנזקציות (אם בסיס הנתונים תומך) ולהיות חלק מאותה פעולה אטומית בשלבים 2.1 ו 2.2.

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

  • יכולים להיות מגוון דרישות כיצד לחשב את ה Limit של כל לקוח. כל דרישה – עם פתרונות אפשריים משלה. יש מגוון אלגוריתמים ל Rate Limiting בניתן למצוא באינטרנט. העניין הוא לספק צרכים ספציפיים – ובאופן הפשוט ביותר שניתן. אין טעם להשתמש באלגוריתם מורכב ממה שנדרש – זו לא הנדסה טובה.
  • מה קורה במידה ויש לנו Scale אדיר, שבסיס נתונים יחיד לא יוכל לעמוד בו? אולי – ה latency של בדיקת המונה (קריאה לבסיס נתונים ע"פ מפתח היא לרוב פעולה של מילי-שניות בודדות) יקרה מדי?
    • בסיס נתונים רלציוני לרוב יכול לטפל בכמה אלפי בקשות פשוטות בשנייה. אם צריך יותר ניתן לנהל אולי 2 רמות של עדכונים: אחת ברדיס מהירה, והשנייה מתוזמנת ומסנכרנת, נניח כל דקה, את הנתונים מרדיס לבסיס הנתונים. כאן נוצר הסיכון שלקוח שחרג מחשבונו ייחסם רק דקה מאוחר יותר (במקרה הגרוע). האם זו פשרה סבירה שניתן לקחת? אולי החברה יכולה לספוג סטיות כאלו – בכדי להשאיר את המערכת יחסית פשוטה?
    • המקרה של latency הוא דיי דומה. אפשר לנהל שכבר מהירה ושכבה איטית. אפשר להפוך את פעולות העדכון לאסינכרוניות ואפשר להחזיק cache מקומי על המונה וכאשר הוא מתקרב לקצה (נניח: 95%) רק אז להחיל בדיקה בכל קריאה. עוד אופציה חלופית לזו היא לנהל "כרטיסיות" של גישות כאשר שרת מבקש מבסיס הנתונים בקשה ל 100 בקשות ואותן הוא מנהל בזיכרון ורק כל 100 קריאות פונה לבסיס הנתונים. יש כאן מספר מקרי קצה שדורשים טיפול – גישה אפשרית אך לא מאוד פשוטה.
      • בכל מקרה נראה שצמצום latency ו/או הרחבת scale יגיעו עם tradeoff לגבי הדיוק של האכיפה. חשוב לזכור שהרבה מורכבות טכנית יכולה להיות מומרת בגמישות עסקית, למשל הסכמה לחריגה של לקוחות בכמה אחוזים מההקצאה היומית שלהם – על חשבון החברה.
  • מכיוון שמדובר בכסף, חשוב לבדוק את מקרי-הקצה השונים ולראות שאין פספוסים משמעותיים. אני לא חושב כרגע על בעייה עקרונית, אך הייתי משאיר את העניין כנקודה לבחינה.

סיכום

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

  • תהליך בניית תוכנה (גם תכנון וגם קידוד) הוא תהליך של גילוי, חשיבה, ולמידה מתמשכים. זו טעות לקפוץ למסקנות מהירות, להחליט מוקדם ולהתקבע על כיוונים ופתרונות. לסגור אופציות. בטח שגם לממש דברים שלא ברור לנו שהם נחוצים.
    • כמעט כל "הפתרונות" שסקרתי בווב (בעיקר עבור כתיבת הפוסט הקודם: URL Shortener) היו כאלו: החלטיים, "יודעים", ומציגים תמונה של פתרון כאילו זה תרגיל בטריגונומטריה, עם התחלה וסוף חד-משמעיים. זה לא עובד כך באמת.
  • אם היה תכנון "מיטבי" (מלה שדיי צורמת לי, אגב, כשאני שומע אותה בהקשרים מקצועיים) לסגנון של בעיות: Cache, URL Shortner, או Rate Limiter – אז היה כבר Open Source אחד לכל בעיה שמשתמשים בו ודי.
    • אני מקווה שהצלחתי להדגים כיצד כל מצב עסקי מעט שונה – יהנה מתכנון שהוא קצת או הרבה שונה. ששתי הבעיות בפוסט – באמת דורשות פתרונות דיי שונים.
    • ברור שלפעמים נכון להתפשר, ולקחת פתרון קצת פחות מתאים – כי הוא זמין, אמין, או קל. אני עדיין בעד שימוש חוזר בשירותים וספריות (קוד פתוח, או בכלל).
  • טעות נפוצה שלישית היא צלילה לפרטים לפני בניית "Walking Skeleton" (קרי "שלד מהלך"). במקום לצלול לפרטים ובעיקר למימוש של סכמת בסיס-הנתונים (לרוב דומיין ידוע, שילמד אותנו מעט על התכנון והצרכים, אבל יקשה על שינויים מהותיים בתכנון) – חשוב יותר להרכיב "Flow" עובד מקצה לקצה (להלן: שלד) וגם לאמת אותו עם העולם – למשל להריץ עליו כמה בדיקות או אפילו תעבורה אמיתית של המערכת (להלן: מהלך).

אני מקווה שהצלחתי במשימה.

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