כל צוות הנדסה רוצה מערכת שצומחת ללא קשיים. אנחנו מדמיינים תנועה עולה בצורה חלקה, שרתים מזמזמים והכנסות עולות בהדרגה. ואז המציאות מכה. קמפיין שיווקי ויראלי שולח גל של משתמשים, מסד הנתונים ננעל, ומישהו מנסה בטירוף להפעיל מחדש שירותים בשלוש לפנות בוקר. התגובה האינסטינקטיבית היא להאשים את הכלים. אנחנו אומרים לעצמנו שהיינו צריכים יותר ליבות, דיסקים מהירים יותר או שכבת מטמון נוספת. אך צמיחה אינה נובעת מחומרה. היא נובעת ממבנה. אם התשתית שלכם אינה יכולה לחלק את העומס, כל משתמש חדש הופך לנטל במקום לניצחון.
למה כלים לא יכולים להציל תשתית שבורה
אתם יכולים להקים מאות מופעי ענן (instances), להוסיף מאזני עומסים (load balancers) בין אזורים גיאוגרפיים, ולמטמן כל נכס סטטי ברשת הפצת תוכן (CDN) גלובלית. אלו הם מכפילים של כוח. אך כפל של אפס עדיין נותן אפס. אפליקציה מונולית עם תלויות סבוכות תיחנק תחת משקלה שלה, לא משנה כמה חומרה תהיה מתחתיה.
דמיינו חנות מקוונת שבה קטלוג המוצרים, עיבוד התשלומים ואימות המשתמשים כולם חיים בבסיס קוד (codebase) יחיד. כאשר תהליך התשלום מואט, האתר כולו נסחף. דף ההתחברות מגמגם. חוויית הגלישה נפגעת. אי אפשר להגדיל את צוואר הבקבוק מבלי להגדיל את כל שאר המערכת יחד איתו. זה יקר, לא יעיל ושברירי. אתם מסתיימים בתשלום על כוח מחשוב שלא מועיל לאיש, בזמן שהמשתמשים שלכם מחכים לדפים שהיו אמורים להיטען באופן מיידי.
ארכיטקטורה היא התשובה למלכודת הזו. היא השלד הבלתי נראה שקובע האם הכלים שלכם יעזרו או יזיקו.
מה ארכיטקטורה איתנה באמת אומרת
ארכיטקטורה איתנה היא פשוט תוכנית לקביעת מקום האחריות. היא שואלת שאלות לא נוחות בשלב מוקדם. מה קורה כשחלק אחד נשבר? האם ניתן לשנות את לוגיקת החיוב מבלי לגעת במנוע ההמלצות? האם זרם תנועה פתאומי בפינה אחת של האפליקציה יכול להשאיר את שאר המערכת פועלת כרגיל? שאלות אלו חשובות הרבה יותר מהבחירה בשפת התכנות, בפריימוורק או בספק הענן שלכם.
ארכיטקטורה טובה נותנת לכם מרחב לשינוי דעה. היא מגדירה גבולות ברורים כך שניסוי של צוות אחד לא יערער את עומס העבודה בייצור (production) של צוות אחר. היא מתייחסת לכשל כתנאי פעולה רגיל במקום להפתעה. כשמתכננים תוך התחשבות בכשל, מפסיקים לבנות בתים מזכוכית ומתחילים לבנות מבנים גמישים.
מיקרו-שירותים (Microservices) כתבנית מעשית
דרך מעשית אחת להשיג סוג כזה של מבנה היא לפרק את האפליקציה שלכם למיקרו-שירותים. במקום בסיס קוד אחד ענק, אתם מחלקים את האפליקציה לחלקים קטנים. כל חלק מטפל במשימה ספציפית אחת. שירות התשלומים מעבד עסקאות. שירות המלאי עוקב אחר המלאי. שירות ההתראות שולח אימיילים והודעות טקסט. הם מתקשרים באמצעות ממשקים (interfaces) מוגדרים במקום גישה ישירה לזיכרון או טבלאות מסד נתונים משותפות.
הפרדה זו יוצרת מרחב תמרון אמיתי, הן מבחינה טכנית והן מבחינה ארגונית.
עדכון חלקים קטנים מבלי לשבור את המערכת כולה
כאשר השירותים קטנים וממוקדים, ניתן לתקן חלק אחד מבלי להסתכן בכשל שרשרת. אם הצוות שלכם מגלה באג באלגוריתם חישוב המשלוח, אתם מתקנים את השירות הזה ומטמיעים (deploy) אותו בנפרד. שאר האפליקציה ממשיכה לפעול. המשתמשים עדיין גולשים במוצרים, עדיין מתחברים, ועדיין מוסיפים פריטים לעגלת הקניות שלהם. רדיוס הפגיעה של כל שינוי בודד נשאר קטן מאוד. השוו זאת למונולית שבה טעות הקלדה בפונקציית עזר יכולה לשבור את התשלום, את ההרשמה ואת הדיווחים – הכל בבת אחת.
הרחבת פונקציות ספציפיות כאשר התנועה עולה
התנועה לעולם אינה אחידה לאורך כל האפליקציה. במהלך מכירת בזק, צינור ההזמנות שלכם עשוי להיות תחת עומס, בעוד שמערכת ניהול התוכן שלכם כמעט לא פעילה. במערכת צמודה (tightly coupled), אתם מרחיבים הכל או כלום. עם מיקרו-שירותים, אתם מכוונים את המשאבים שלכם בדיוק. הקימו יותר מופעים של שירות התשלום. תנו לקטלוג המוצרים לרוץ בצריכה הרגילה שלו. במהלך השקת מוצר, ה-workers של עיבוד התמונות שלכם עשויים לעמוד בתור עם אלפי תמונות מוקטנות, בעוד אינדקס החיפוש נשאר רגוע. אין סיבה להרחיב את אשכול (cluster) החיפוש רק כדי לספק את ה-workers של התמונות. אתם מוציאים כסף במקום שבו המשתמשים מרגישים אותו, והמערכת שלכם נשארת קשובה תחת לחץ.
פריסת קוד חדש ללא זמני השבתה ארוכים
שירותים קטנים מאפשרים דפוסי פריסה שהופכים את חלונות התחזוקה (maintenance windows) למיותרים. ניתן להשתמש בפריסות rolling deployments, על ידי דחיפת קוד חדש לתת-קבוצה של מופעים (instances) בעוד השאר ממשיכים לשרת תעבורה. עקבו אחר שיעורי השגיאות שלכם, ואם משהו נראה לא תקין, הפנו את הבקשות חזרה לגרסה הקודמת תוך שניות. פריסות blue-green מאפשרות לכם להקים סביבה חדשה לחלוטין, לאמת אותה, ולהעביר אליה את התעבורה בסיכון מינימלי. המערכת לא צריכה להיעלם למשך שעות בזמן שמישהו מריץ מיגרציות מסד נתונים באופן ידני.
בניית פיצ'רים חדשים מהר יותר
בסיסי קוד גדולים מולידים זהירות. שינוי בודד דורש הבנה של אלפי שורות לוגיקה לא קשורה, בדיקות רגרסיה שלוקחות שעות, ולוחות זמנים של פריסה שמרגישים כמו שיגור רקטות. שירותים קטנים מסירים את הפחד הזה. צוות יכול לבנות פיצ'ר חדש על ידי שינוי של כמה מאות שורות בשירות שהוא מכיר לעומק. הם מבצעים commit, בודקים ומשחררים באותו היום. המהירות הזו מצטברת. כאשר שירותים מוגדרים על ידי אחריות ברורה, צוותים מפסיקים לדרוך על עבודתם של אחרים. הם מחזיקים בבעלות על הדומיין שלהם מקצה לקצה.
עצמאות מונעת שיבושים משמעותיים
כל שירות עובד בפני עצמו. העצמאות הזו אינה רק נוחות ארגונית; היא ביטוח מבני. אם מנוע ההמלצות קורס, החנות עדיין צריכה למכור מוצרים. אם צינור האנליטיקה (analytics pipeline) נתקע בגלל אירוע (event) לא תקין, שירות ההתחברות עדיין צריך לאמת משתמשים. אתם מתכננים circuit breakers ונתיבי fallback בין שירותים כדי שכישלון אחד לא יתגלגל לכדי קריסה מלאה. המערכת גדלה לצד המשתמשים שלכם כי היא יכולה לספוג עומס מבלי להתפרק בחיבורים שלה.
מילת אזהרה: אל תפצלו באופן עיוור
שום דבר מזה לא אומר שעליכם לפצח את בסיס הקוד שלכם ביום הראשון. מיקרו-שירותים (microservices) דורשים גבולות ברורים. אם הצוותים שלכם עדיין לא יודעים איפה דומיין אחד נגמר ואחר מתחיל, הם ייצרו בלגן מבוזר במקום מערכת מבוזרת. אתם תחליפו מורכבות קוד במורכבות תפעולית, ופתאום תצטרכו לנהל שיהוי רשת (network latency), טרנזקציות מבוזרות, retry storms ו-observability על פני עשרות זרמי לוגים. ניפוי שגיאות (debugging) של תהליך צ'ק-אאוט איטי יכול כעת להפוך למעקב אחר בקשה בודדת לאורך ארבע קפיצות רשת ושלושה מאגרי נתונים שונים.
אם הצוות שלכם לא מוכן ל"מס" הזה, התרופה גרועה מהמחלה. לפעמים הצעד החכם יותר הוא להתחיל עם מונוליט מודולרי (modular monolith). שמרו על לוגיקת תשלומים נפרדת מלוגיקת מלאי בתוך בסיס הקוד, גם אם הם נפרסים יחד. אכיפו גבולות באמצעות APIs פנימיים וסכמות מסד נתונים נפרדות בתוך אותו מנוע. כאשר התפרים הללו מוכיחים עצמם כיציבים ותבניות התעבורה מצדיקות את העלות הנוספת, חלצו שירות. ארכיטקטורה צריכה להיות סדרה של דלתות מכוונות, לא קירות שנבנים בן לילה כי קראתם פוסט בבלוג.
התחילו עם כוונה
ארכיטקטורה איתנה אינה עוסקת בחיזוי תעבורה בחמש השנים הבאות. היא עוסקת במתן אפשרויות לעצמכם. אינכם יכולים להסתמך על כלים בלבד כדי להגדיל את אפליקציית הווב שלכם, אך אתם יכולים לחשוב בדרך לפתרון בעיות לפני שהלחץ גובר. כבדו את הגבולות בין תחומי אחריות. בנו חלקים קטנים וממוקדים ששולטים בגורלם. תנו לצוותים את האוטונומיה לנוע מהר מבלי לשבור את השלם. כשמתחילים עם ארכיטקטורה איתנה, חוסכים זמן ומאמץ בהמשך כי לא כותבים מחדש לוגיקת ליבה בזמן שהאתר בוער.
השורה התחתונה
סקיילביליות אינה פיצ'ר שמוסיפים כתוספת חיצונית כשהצמיחה מגיעה. היא התוצאה הטבעית של בחירות שעשיתם בשלב מוקדם לגבי האופן שבו האחריות זורמת במערכת שלכם. בחרו את התפרים הנכונים. בודדו כשלים. הגדילו את מה שיוצר קושי, והשאירו לבד את מה שעובד. עשו זאת, והכלים שתצטרפו מאוחר יותר יהיו בעלי בסיס איתן להישען עליו.
