2019-11-17

Refactoring 2020

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

הספר Refactoring טבע כמה מושגי יסוד שהושרשו עמוק בתעשייה כמו Refactoring ,Code smells, או Testability של קוד. החלק החשוב בספר הוא לא קטלוג ה Refactoring (הטכני), אלא 4 הפרקים הראשונים העוסקים בכמה רעיונות מאוד חשובים בפיתוח תוכנה:
  • בדיקות-יחידה (Unit-Tests), היו אז עוד רעיון חדשני ולא כל-כך מוכר. 
    • שווה לציין שיותר ממה שבדיקות יחידה פותרים באגים, הם מאפשרים לעשות Refactoring במהירות ובביטחון.
  • הרעיון שהשלב הראשון בפתירת באג, היא לכתוב בדיקה שתיכשל - ואז לגרום לבדיקה לעבור (מה שיוכיח שהבאג תוקן, ויוודא שאין רגרסיה והוא חוזר). 
  • הרעיון שניתן לשנות/לשפר Design של מערכת קיימת אחרי שהקוד נכתב. 
    • הרעיון הזה תומך ומחזק רעיונות כמו Minimum Viable Products (בקיצור: MVP) ו YAGNI - רעיונות סופר-חשובים ומשמעותיים.
    • הרעיון הזה, לצערי, לא חלחל לכל קצוות התעשייה עד היום. 
  • הרעיון ש Refactoring צריך להיות חלק מובנה מתהליך פיתוח תוכנה, כמו ש Delivery הוא כזה. כמובן שבלי delivery אין פיצ׳רים ללקוחות, בעוד refactoring ניתן לדחות ולדחות עד ה Rewrite הבלתי נמנע.
    • בספר פאוולר מקדיש חלק ל״איך לשכנע את המנהל״ לבצע Refactoring. עצה אחת היא שהמנהל לא יאהב את רעיון ה Refactoring, אבל אם האלטרנטיבה היא Rewrite - אז הוא יעדיף אותה. עצה שניה היא פשוט לא לספר למנהל - אלא פשוט לעשות. הדילמה הזו רלוונטית גם היום.




האם רעיון ה Refactoring באמת הצליח?


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


האם באמת אנו פועלים כך ש Refactoring הוא חלק בסיסי מהעבודה?

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

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

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


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

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

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


Software Death Spiral
ביצוע Refactoring אמור להחזיר אותנו מעט לאחור - ולהאריך את קץ המערכת...



האם חשוב לעשות Refactoring?

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

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

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

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

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

כמובן שהעניין תלוי בגורמים שונים: עד כמה משמעותיים היו השינויים בביזנס? עד כמה השקענו ב Refactoring במהלך חיי המערכת? עד כמה המערכת מורכבת. לא ניתן לקבוע מדד אוניברסלי לאורך חיי מערכת.

בכל זאת Refactoring הוא תהליך של הארכת חיי המערכת - והוא תהליך חיוני לארגונים שרוצים להיות יעילים.



אז למה לא לעשות Refactoring?

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

למה?

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

״תנו לנו 3 שבועות, נבצע Refactoring באזור ההוא - ואז הכנסה של פיצ׳רים תהיה קלה בהרבה!״

אבל המנהלים, שהם אמונים על ובקיאים בתמונת הפרויקט, רואים לעתים ש:
  • ה Refactoring קצת הסתבך וארך בעצם כמעט 5 שבועות.
  • פיצ`רים לא תמיד יותר מהירים לאחר ה Refactoring, לפחות לא משמעותית: ״כן, ה Refactoring שיפר מהירות של פיצ`רים מסוג z, אבל זה פי׳צר מסוג y...״
  • ה Refactoring עלול לגרום לרגרסיות במערכת ו/או באגים נוספים.
באמת, ממעוף הציפור, ומבלי להכיר את הפרטים - Refactoring אכן עשוי להישמע כדבר שרצוי להימנע ממנו, ולדחות אותו.


מי צודק? המנהלים או אנשי-התוכנה?



למה Refactoring נכשלים?


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

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

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

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



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

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

מהצד השני חשוב לזכור ש:
  • מהנדסים מאומנים לזהות Code Smells, להבין כיצד הקוד היה צריך להראות - ואז לרצות לשפר את הקוד. טוב מאוד שכך (!!), אבל זה גם אומר שיהיה רצון לבצע בקוד גם שיפורים בעלי עלות/תועלת נמוכה מאוד בראייה מעשית.
  • לא כל Refactoring מייצר קוד טוב יותר. יש סיכון שה Refactoring יוביל לעיצוב מערכת פחות מוצלח, יהיה ניתן להבין את זה רק לאורך זמן - ואז כבר יהיה מאוחר מדי בכדי להתחרט.
  • Refactoring לא עוזבים באמצע. לפעמים אנו מתחילים Refactoring של שבוע - שאורך שבועיים ואף שלושה שבועות.
  • אין גבול לזמן שניתן לעשות בו Refactoring. זכיתי להזדמנות הנדירה לחזות ב Refactoring של צוות קטן לאורך חצי שנה. השינויים שנעשו באמת היו משמעותיים - שינו את מודל הליבה של המערכת למודל שונה למדי. סידרו הרים של קוד.
    לרוע המזל, לא נראה שמשהו מהותי באמת השתפר. זה היה שונה, אבל קשה מאוד היה לומר שזה היה טוב יותר.


יאללה, המלצות (סיכום)


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

האם מפסיקים לעשות Refactoring ומתכננים כבר ב Schedule מועד ל Rewrite לכל חלקי המערכת?
האם פשוט צריך להחליף את ההנהלה? להחליט שימי ראשון מוקדשים ל Refactoring בלבד?

הנה כמה המלצות קונקרטיות, ממיטב ניסיוני:
  • Refactoring גדול = סיכון גדול, Refactoring קטן = סיכון קטן. 
    • דוגמה קלאסית ל Refactoring עם ROI חיובי ברור הוא Rename לאובייקט מרכזי במערכת שהשם שלו מטעה / פשוט לא נכון. ההשקעה היא במסגרת הדקות - התמורה היא ודאית.
    • נסו ליצור תרבות של Refactoring קטנים עליהם לא צריך לספר למנהל (כפי שהציע פאוולר), ולא צריך לשנות בשבילם Schedule של פיתוח. סיכוי טוב שה ROIs יהיה מוצלח, וגם תהליכי ה Code Review יוכלו לספק בקרה ופידבק לגבי הנכונות ההנדסית של ה Refactoring הללו.
  • כאשר אתם עושים Refactoring גדול, התייחסו אליו כמו כל פיצ׳ר גדול ומסוכן אחר:
    • עשו בקרה לעיצוב המוצע. למשל: Design Review.
    • נסו ברצינות לפרק אותו לשלבים קטנים יותר / לבצע MVP שיספק פידבק מוקדם יותר עד כמה השינוי מוצלח/יעיל.
    • נסו לצמצם אותו ולחשוב: מה אפשר להוריד מהמודל היפה והאלגנטי הזה שמוצע. על אלו פרטים ניתן לוותר - בלי לאבד הרבה מהערך. מניסיון - זה עובד.
  • הכי מסוכן זה Refactoring גדול של מישהו שקיבל לאחרונה אחריות על קוד. למשל: חודש-חודשיים אחרי שצוות מקבל קוד מצוות אחר, הוא ״מיואש״ כבר מהקוד - ודורש לבצע Refactoring גדול.
    • יש בכך יתרון: תוך Refactoring כזה, הקוד בהדרגה "יהפוך לשל הצוות": הוא יהיה מוכר יותר והצוות ירגיש שלם יותר איתו. Refactoring היא דרך מעולה להבין קוד לעומק.
    • הסיכון הגדול הוא שהצוות לא מבין את הדומיין טוב מספיק עדיין, וסיכון גדול שהוא יבצע שינוי ל Design שאינו טוב יותר.
    • נסו למנוע / לצמצם עבודת Refactoring גדולה של מישהו שלא עבד עם הקוד חצי שנה לפחות. כן עודדו לעשות כמה שיותר Refactoring קטנים (כמה שעות כ״א, למשל). זה כלל האצבע שלי.
  • אל תוותרו על Refactoring, גם אם זה קצת מורכב:
    • Refactoring הוא אוויר לנשימה למערכת. מערכות דורשות חמצן והתחדשות - ותהליך ה Refactoring מספק להן את זה.
    • לא פחות חשוב: Refactoring הוא אויר לנשימה למהנדסים שעובדים על המערכת. 
      • זה ממש רע לקבל, ולהתרגל לעיוותים וחוסרי-הגיון בקוד - זו דרך טובה להפוך למהנדסים פחות טובים.
      • Refactoring הוא גם רענון שכלי למפתח (שאנו לא רוצים ״שיתייבש״): עבודה הנדסית שקצת שונה לפעמים מבוספת פיצ'רים למערכת.
      • Refactoring עוזר לקבל רמת עומק גבוהה יותר עם הקוד שמתחזקים, במיוחד לאורך זמן (״הנה המשמעות של מצב A, מול המשמעות של מצב B״)


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


8 תגובות:

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

    השבמחק
    תשובות
    1. מסכים, הסכמה מסביב ל Refactoring היא לא דבר קל..

      מחק
  2. מה זה Refactoring ?

    השבמחק
    תשובות
    1. כתבתי לא נכון?
      אם לא - חפש באינטרנט. שווה לך.

      מחק
  3. מעולה
    מזכיר לי מאמר מענין (וישן מאד) של יואל ספולסקי על rewrite של קוד שרלוונטי גם בנושא הזה
    https://www.joelonsoftware.com/2000/04/06/things-you-should-never-do-part-i/

    השבמחק
  4. בהחלט רלוונטי. תודה בה על השיתוף!

    השבמחק
  5. מספר תהיות:
    1. האם בכלל צריך להסביר את משימת הREFACTOR למנהלים? הרי בהגדרה הם לא מסוגלים, לדעתי, להבין את הROI של התהליך הזה (אלא אם כן אנחנו נמצאים כבר בסטגנציה מלאה של תהליך הפיתוח, והמנהל נואש ומוכן יהיה לנסות כל מה שיציעו לו).
    2. אני פוגש בתעשיה יותר ויותר מקרים בהם אירגוני פיתוח, בצורה הומוגנית, מאמינים שיש דבר כזה תיכנון מושלם, ולכן אין להם יכולת להבין שניתן לשנות design של מערכת קיימת. התוצאה? בוא נעשה complete rewrite תוך כדי תחזוקה של המערכת הישנה. לא רק שזה יקר, אלא גם תוקע את ההנהלה בבעיה - ההנהלה לא תסכים לכבות את המוצר הישן כל עוד החדש לא עושה כל מה שהישן עשה. בעצם הם נעולים בנקודה בה יכאב מאוד לכבות את המערכת הישנה.
    3. חשוב לשים את הדגש על unit tests - בלעדיהם אי אפשר לייצר ביטחון בתהליך שכן אתה רוצה להיות בטוח שהrefactor לא שבר משהו מהותי. לצערנו -הכלי היחיד האפקטיבי הוא בדיקות אוטומטיות (יחידה, אינטגרציה או e2e בתמהיל כזה או אחר). אי אפשר להתחיל לבצע refactor ולהסתמך על בדיקה ידנית - כי אז התהליך (או הפידבר על השינוי) הוא איטי - איטי מידי מכדי שהrefactor יהיה בר ביצוע.

    חוצמזה מאמר מעולה! בובה של טיפים!

    השבמחק
  6. היי חנוך,

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

    תודה על הנקודות!

    השבמחק