אל תפחדו מקוד ספגטי בתוך פרויקט אוטומציה | אלכס קומנוב

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

 

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

 

·          פעם אחת על מנת לתקן המון טסטים נופלים.

 

·          פעם שנייה בשביל להטמיע תשתית חדישה יותר. 

 

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

 

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

 

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

 

קוד ספגטי קיים אצל כולם

 

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

 

• התאריך הוא 25 בינואר לשנת 2023. לרשת דולפים כ-45GB של קוד מקור של מוצרים שונים מחברת ענק (בתחום ה-IT) הרוסית YANDEX . 

• מרץ 2023 הייתה  דליפת קוד בחברת X (טוויטר של הימים ההם). 

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

 

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

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

 

ופה נשאלת השאלה - האם האפשרות שמנוע חיפוש של גוגל כתוב בצורה לא איכותית - הפריעה לכם אי פעם ובגלל זה חדלתם להשתמש במוצר הזה או בכל מוצר אחר של חברת גוגל? מניח שאם כבר אתם מעדיפים מוצר אחר - זה בעיקר בגלל נוחות השימוש (מה שמכונה User Experience) של מוצר אחר, ולא בגלל קוד לא איכותי. 

 

תכתבו קוד באחד משני הכובעים

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

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

• ניסיון לחשוב ולטפל בכל המקרים ותגובות.

• שיפור בקוד (מה שמכונה רפקטורינג) הקיים תוך כדי כתיבת קוד חדש.

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

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

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

1710764747-5416

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

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

• כובע נוסף - שיפור הקוד, מה שמכונה רפקטורינג. שיפור של הקוד הקיים.

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

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

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

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

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

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

עקרון של DRY - ראשי תיבות של Don't Repeat Yourself. העקרון מדבר על זה שצריך להימנע מכתיבה חוזרת של הקוד ובמקום זה לעשות שימוש חוזר בפונקציונליות שכבר נכתבה.

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

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

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

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

 

קודם כל תגרמו לקוד לעבוד

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

“1. “First make it work.” You are out of business if it doesn’t work. 2. “Then make it right.” Refactor the code so that you and others can understand it and evolve it as needs change or are better understood. 3. “Then make it fast.” Refactor the code for “needed” performance.”

אנחנו צריכים לחלק את העבודה ל-3 חלקים:

1. לגרום לקוד לעבוד.

2. לכתוב את הקוד נכון.

3. לשפר את הביצועים.

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

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

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

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

 

כתיבת קוד זה מסע למידה

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

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

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

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

 

היצמדו לכללים הקיימים

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

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

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

במקום סיכום

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