-->

יום שבת, 3 באוגוסט 2013

מדוע אנו זקוקים ל Software Design?

בפוסט זה אני רוצה לחזור לבסיס ולסקור מדוע אנו זקוקים לפעולת ה Software Design.

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

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

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



להלן סדרת בעיות התוכנה, ש Design יכול למתן:



בעיית ההתמצאות (Orientation)

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

דרכים אלו גוזלות זמן ומהוות waste שנרצה להימנע ממנו.

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


בעיית ההקשר (Context) / הגבולות הברורים (Boundaries)
תת-בעיה של בעיית ההתמצאות.

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

כאשר ההקשר ברור (למשל: "אנו באזור ה Business Logic של ניהול ההזמנות, שלא נוגע ב DB או ב UI") - קלות קריאת הקוד משתפרת, גם ללא שינוי הקוד עצמו. נוכל לקרוא קטע קוד באופן עצמאי, מבלי לקרוא את הקוד שמסביבו.

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





בעיית "נגעת - שברת" (Software Fragility)

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

האם אפשר לתכן תוכנה אחרת, כך שהסיכוי "לגרום לשבירה" יפחת?

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



בעיית הדומינו (Software Rigidity)

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

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

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




בעיית ה Development Scalability

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

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

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

לבסוף, אנו רוצים שאינטגרציות יכללו מינימום "התנגשויות" - ומכאן יצטמצמו פעולות ה merge ברמת ה source control. בשביל ה productivity.




בעיית ה Deployment

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

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

לעדכון של מערכת גדולה יש עלות משמעותית: אפשר לעבוד ימים, אם לא שבועות בכדי לעדכן מערכת גדולה ומורכבת.
כאשר יש Persisted Data, שינוי במבני הנתונים יכול לדרוש תהליך של migration: תהליך שיכול לארוך שעות של downtime ולא-תמיד הוא נקי מתקלות.

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

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



בעיית הפריון (Productivity)

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

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

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



בעיית הסיבוכיות

זוהי מטא-בעיה שמסכמת את הבעיות שציינו:
  • בעיית ההתמצאות
  • בעיית ההקשר
  • "נגעת-שברת"
  • בעיית הדומינו
  • בעיית ה Development Scalability
  • בעיית ה Deployment

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


סיכום ביניים

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

על מנת שנוכל להתאים כלי של Design לבעיה, כדאי לרדת מרמת הדיון על "סיבוכיות" ולדון ברמת בעיות המדויקות יותר: development scalability, בעיית ההתמצאות או בעיית Deployment.

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


אני לא יכול שלא להזכיר בנושא זה סוג נוסף של "בעיה":



השאיפה לשימוש-חוזר (Reusability)

שימוש-חוזר (Reusability) הוא אחד הדברים שהכי קל "למכור" בצוות / בארגון: "למה לכתוב אותו הקוד פעמיים? בואו נכתוב פעם אחת ונשתמש בו בשני המקומות!"

יש לי הרבה מה לומר בנושא, אך אנסה לקצר בעזרת 2 חוקים בנושא, חוקי ה 3x3 של השימוש-החוזר:
  1. כתיבת רכיב המתאים לשימוש חוזר היא פי 3 יותר קשה מכתיבת רכיב לשימוש יחיד [א].
  2. רק לאחר שרכיב בשימוש ע"י 3 מערכות שונות - הוכחה היכולת שלו להתאים לשימוש חוזר.
אמנם יש מקרים בהם שימוש-חוזר הוא פשוט יותר, אבל לרוב מדובר במצב בו רכיב בודד צריך לטפל בכפליים תסריטים וכפליים מאפייני איכות - מאפיינים ברורים המגדילים את הסיבוכיות שלו. התסריטים ומאפייני האיכות הנוספים לא תמיד צצים בבחינה ראשונית של הנושא, במיוחד אם זו בחינה בגובה "10,000 רגל", שלא נכנסת לפרטים.

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

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



סיכום

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

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


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


---

[א] אין כמובן דרך למדוד "פי 3 יותר קשה", במיוחד כהכללה. הכוונה היא כמובן לומר: "הרבה יותר קשה".



2 comments: