2018-06-16

גם Data Science "בשקל" - יכול להיות שווה הרבה! (על Tableau)


נתונים של מערכת הם לרוב משאב שלא מוצה.

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

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

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


בעקבות הטרנדים החמים היום של "Big Data" ושל "AI/ML" - מפתחים רבים מחליטים להשקיע ולהעשיר את הידע שלהם בכיוונים של Data Science.
לפעמים זה ידע תאורטי, לפעמים זו התנסות בסיסית ביצירת רשת ניורונים או Random forest. 

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


אני חושב שזו טעות טקטית נפוצה:
  • Data Science, בעיקרML ובמיוחד Deep Learning - הם תחומים עם עקומת למידה תלולה למדי, עדיין.
    • איש תוכנה יכול להשקיע עשרות ומאות שעות בלמידה ופיתוח skill - שעדיין יהיה בסיסי מאוד, יחסית למי שעוסק בתחום במשרה מלאה. לא יהיה לאיש התוכנה יתרון יחסי ברור מול איש-מקצוע ב Data Science, במיוחד לא כזה עם ניסיון לא-מבוטל.
    • אני מעריך שככל שהזמן יעבור - יהיה קל יותר ללמוד וליישם פתרונות Data Science, כך שייתכן ש 100 שעות למידה היום - יהפכו ל 20 שעות למידה בעוד 5 שנים. חלקים רבים מהידע שנלמד היום - יהפכו לכמעט-לא-חשובים, עבור מגוון נפוץ של יישומים / בעיות.
  • דווקא שיטות "פחות-מתוחכמות" של Data Science עשויות להניב לאיש התוכנה יתרון יחסי: שיטות כגון שאילתות SQL, סקריפטים שמעבדים ומנתחים נתונים, או כלי ויזואליזציה. 
    • התחומים / שיטות הללו מפותחים כבר מאוד - קל ללמוד אותם מהר, ויש מגוון רחב מאוד של כלים ופרקטיקות שתומכים בהם.
    • יש כאן יתרון יחסי ברור של איש תוכנה המכיר את המערכת מקרוב:
      • הוא מבין את הנתונים (או לפחות חלקים מהם) - בצורה עמוקה.
      • נתון שאינו מכיר - הוא יכול למצוא את הקוד וללמוד בדיוק כיצד הוא מתנהג.
      • הוא יכול להוסיף נתונים ולטייב נתונים, ולהבין בצורה מהירה מה המורכבות של שיפור / טיוב נתונים שכאלו. 
        • מה הבעיה ללכת לקוד ולבצע עוד כמה בדיקות / להזיז מקום את הקוד שאוסף נתונים - כך שיהיה מדויק יותר? - לאיש Data Science זוהי מלאכה קשה מאוד.


ארצה להציג דוגמה לשימוש בכלי Data Science "פשוט", שאינו קשור ללמידת מכונה או "Big Data". ספציפית, אסקור כלי בשם Tableau שאני משתמש בו לאחרונה.




Workbook לדוגמה מ Tableau Public
מקור: https://public.tableau.com/en-us/s/gallery/books-made-movies



למה טאבלו (Tableau)?


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

אין דרך טובה לנהל את השאילות, ורבים מאיתנו מנהלים קובץ בצד שבו רשומות שאילתות SQL שאנו מעתיקים ומדביקים בכדי להריץ.

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


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

כלים דיי ידועים הם MyDBR או Redash (הישראלי / של יוצא GetTaxi) - שהם טובים ופשוטים, וקל מאוד להתחיל לעבוד איתם בזמן קצר.

אני אכתוב על Tableau שהוא "כלי BI", כלומר שהוא יקר יותר (תשלום ע"פ מספר משתמשים, 30-70 דולר בחודש למשתמש), וההטמעה שלו היא מורכבת יותר.

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

בחרתי לדבר דווקא על Tableau כי זה כלי שנבחר לעבודה במקום העבודה הנוכחי שלי. יש לנו גם Redash - אבל בטאבלו אפשר לעשות יותר.
יש עוד סדרה של כלים דומים ל Tableau, כמו MicroStrategy, Qlik, או SiSense (גם חברה ישראלית). הכלים הללו, כמובן, הם לא שקולים לגמרי - ולכל כלי יש את החוזקות היחסיות שלו.

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

לטאבלו יש כמה גרסאות, אך אני רוצה לציין את החשובות שבהן:
  • Tableau Desktop - אפליקציית Desktop לזריזות וגמישות מרביים. זה הרישיון היקר יותר.
  • Tableau Server - גרסה וובית ומצומצמת יותר של גרסת ה Desktop. השיתוף הוא קל יותר - והרישיון עולה כחצי מחיר. רישיון של Tableau Desktop כולל גם רישיון ל Tableau Server על מנת לשתף את המידע.
  • Tableau Public - גרסה חינמית של ה Desktop, שניתן להשתמש בה רק מול שרת ציבורי של טאבלו בו הנתונים יהיו נגישים לכל העולם, וכמות מוגבלת של נתונים (15 מיליון רשומות). 

ב Tableau Public אתם יכולים להגביל את הגישה של משתמשים אחרים לנתונים / לקוד המקור (ה Workbook) - אם כי משתמשים רבים מתירים את ההורדה.

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

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



כמה טיפים חשובים על טאבלו שיאפשרו לכם כניסה מהירה יותר


Undo הוא חברכם הטוב 




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


להבין את ההבדל בין Measures ל Dimension

זה קצת מבלבל בהתחלה:
  • Measure הוא נתון שאנו רוצים להציג אותו, או בד"כ - Aggregation שלו (כמו SUM, AVG, אחוזון מסוים, וכו'). מכירות, אחוז הצלחה, וזמן ריצה - הם measures קלאסיים.
  • Dimension הוא נתון שלפיו אנחנו רוצים לפלח את הנתונים ולהציג אותם כשורה / עמודה / אזור בגרף.
    למשל: תאריך (שנה / חודש / יום), מיקום גאוגרפי, קטגוריה, סטטוס (הצלחה / כישלון) וכו' - הם dimension קלאסיים.
איך יודעים מה זה מה? - זה לא כ"כ חד משמעי.

טאבלו ינחש בצורה "תבונית" מה הוא מה - ויקטלג לכם אותם ב Data pane:


טאבלו יטעה מדי פעם - ויהיה עליכם לתקן אותו, ע"י פעולות ה"Convert to Measure" או "Convert to Dimension".

Measures יהיו בד"כ מספרים - עליהם באמת אפשר לבצע Aggregations.
Dimension יהיו בד"כ מחרוזות ותאריכים.

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

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

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

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




להבין את ההבדל בין Columns, Rows, ל Marks

גם זה מאוד בסיסי, אם כי מעט מבלבל בהתחלה.

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

באופן הזה מאוד קל לחשוב על טורים ועמודות כמימדים שונים בהם אתם עושים חישוב - ו marks - כנתונים (measures) שאותם נרצה להציג:



טור ושורה הם שני מימדים שמאוד קל להבין במבנה של טבלה.
הרבה פעמים נרצה 3 ויותר מימדים.

הנה הוספתי לטבלה הנ"ל עוד שני מימדים נוספים:


ההפרדה בין עמודות ושורות היא פחות חשובה, ההפרדה החשובה היא בין מימדים ל Marks, שלרוב יהיו גם מימדים ו measures.


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

Customer הוא אחד ה Segments, ומתחתיו ניתן לראות את השנים.
בויזואליזציות אחרות, הם ההבחנה בין טור ועמודה - עשויים להיראות קצת אחרת.


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

הנה אותה הטבלה בדיוק, כאשר אני מציג כ 3 measures כ marks שונים:


  • סכום העסקאות - כגדול הסימון (עיגול).
  • מספר העסקאות - כטקסט (label). הביטוי CNTD הוא קיצור של Count Distinct.
  • אחוז הרווח - כצבע (gradient), כאשר כחול הוא רווח, וכתום הוא הפסד. כתום גדול = הפסד גדול!
האם זה נכון להציג את סכום העסקאות כעיגול, כמספר? - בוודאי! תלוי במה שחשוב לנו להתמקד בו. אם אנחנו רוצים לאתר מהר אזורים בהם צריך לשפר את המכירות - סכום העסקה הוא החלק הפחות משמעותי.

עוד Marks שניתן להשתמש בהם הוא סוג הצורה (בשימוש ב Shapes) או tool-tip שמופיע ב popup כאשר מצביעים על האיבר.



Show Me הוא כלי חשוב - לא רק למתחילים!

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


בפינה הימנית עליונה נמצא כפתור Show Me - שעוזר לי לדעת למה אני זקוק.
עבור היסטוגרמה, אומרים לספק measure אחד (קל!) - ומודיעים לי שטאבלו ייצור bin field מתאים. יש גם אזהרה שלא כל measure יעבוד.

אני זורק את ה measure של Sales לאחד המימדים (עמודות או שורות - לא משנה) - טאבלו אוטומטית מנחש שאני רוצה לעשות לו aggregation של Sum.
אח"כ אני לוחץ ב Show Me על גרף ההיסטוגרמה - ומקבל את התוצאה הבאה:

הערה: שיניתי את השנתות של ציר ה Y ל logarithmic scale - אחרת היה קשה להבחין בערכים השונים.

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



מה הצבעים ירוק וכחול אומרים?

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

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


למשל: אם ציר ה X שלכם הוא חודש בשנה, ומופיעות שנתות לערכים 0 ו 13 - זה בגלל שהתאריך הוא שדה רציף. הפכו אותו לבדיד - וזה יסתדר.



השתמשו ב Calculated Fields

אופן חשוב בו ניתן לעבד נתונים היא Calculated Fields.
שימוש פשוט הוא פעולה חשבונית כזו או אחרת (חיבור, חיסור, חילוק), אבל אפשר גם לפתור בעיות יותר משמעותיות בטאבלו בעזרת calculated fields.

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



היה לי קשה יותר לבנות את המימד הזה בדרך אחרת / SQL query.

לטאבלו יש רשימה של פונקציות זמינות, ותחביר שמזכיר כתיבת פונקציות ב SQL - בהם ניתן להשתמש ב calculated fields. שימו לב שחלק מהפונקציות זמין רק מול data sources ספציפיים כמו Hive או Google BigQuery (המספקים את הפונקציות בצורה טבעית)

עבור חישובים מורכבים יותר אפשר להשתמש בשפת R - שפת תכנות לכל דבר ועניין.
כדי לכתוב Calculated Fields בשפת R יש להתקין מנוע חישובי בשם RServe המריץ את R. טאבלו ישלח את הנתונים ל RServe - שיבצע את החישוב של השדה הנתון - ויחזיר את התוצאות.

SCRIPT_STR(`substr(.arg1, regexpr(" ", .arg1) -1 )`, ATTR([Business Name ]))

הפונקציה SCRIPT_STR שולחת ל R ביטוי בשפה העטוף במירכאות + פריט המידע שאותו יש לעבד - ומחזירה תשובה מסוג מחרוזת. האינטגרציה היא סבירה - אבל לא מצוינת. למשל: איתור תקלות היה יכול להיות פשוט בהרבה.


השתמשו ב Filters

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

שימוש נוסף חשוב הוא ב Dashboards, כאשר אני מצרף כמה ויזואליזציות ובמקום אחד יכול לפלטר את כולם באותה הצורה. מה שנחמד שה Filters ב Dashboard מופיעים ב View Mode (אם לא סילקנו אותם משם) - וכך הקהל הרחב של המשתמשים יכול להשתמש בהם, מבלי להכנס לעומק ה Data Model.

הנה דוגמה של Dashboard פשוט שיצרתי מ-2 הויזואליזציות הנ"ל:


הוספתי Filter דרך אחד מהויזואליזציות (תפריט = חץ למטה/filters - מציג לי את כל המימדים / measures שבשימוש ולכן ניתן לפלטר לפיהם).


בשלב הבא, אני משייך את הפילטר (דרך התפריט שלו) - לכל הנתונים על ה Dashboard:



עכשיו אפשר לראות שצמצום השנים בעזרת הפילטר - משפיע על כל הנתונים ב Dashboard. איזה יופי!



סיכום


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

הוא כלי רב-עוצמה, אך לא מורכב כ"כ לשימוש.

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

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

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



2018-05-20

הרשו לעצמכם קצת אי-סדר בקוד (דעה)

כשמדברים על סדר ו״ניקיון״ קוד - יש סקאלה של מצבים אפשריים:
  • בקיצוניות אחת: ״בנה ותקן״ בו כותבים את הקוד המיידי ביותר האפשרי בכדי להפעיל את הפיצ׳ר הבא, מגלים באגים ואז הולכים לתקן אותן (וב 20% מהפעמים או יותר יוצרים באג חדש). כל העניין של סדר הוא ״למרובעים״ או ״אנשים בעלי זמן פנוי״.
  • בקיצוניות שניה: פדנטיות קוד, בה כל קבוצת שורות של קוד עוברות refactoring וסידור על בסיס יומי. מקסימום סדר, מקסימום encapsulation, שום גרם מיותר של אי-סדר בקוד.

לכאורה פדנטיות הקוד היא המצב ״הטוב״ ו'בנה ותקן' הוא המצב ״הרע״ ולכן רובנו שואפים לפדנטיות קוד - אבל אף פעם לא מגיעים לשם: מאוד קשה להגיע לשם, ובמיוחד כאשר יש deadlines ו deliveries.

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

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

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

כמו מהירות האור שהיא יעד בלתי-מושג לגוף עם מאסה, כך גם ״סדר קוד מופתי״ הוא יעד בלתי אפשרי לגוף בעל תפיסה-ביקורתית*.

התועלת השולית משיפור איכות הקוד הולכת ופוחתת עם הזמן, אך מחירה הולך ועולה ככל שהקוד איכותי יותר.

--

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


מדע בידיוני, בינתיים.



מדוע בכלל לחתור ל״סדר מופתי״ בקוד?


לקוד של תוכנה יש בעיה גדולה של נראות (visibility): לערמה גדולה של קוד אין צבע, אין ריח, אין תחושה שניתן למשש או מרקם שניתן לראות.

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

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

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

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

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

הבעיה היא שמצב כזה הוא כבר מאוחר מדי - ובד״כ יש צורך בשלב כזה לכתוב את בסיס-הקוד מחדש. באסה!!.


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

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

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

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


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



כוונות מול מציאות



מה עושים?ֿֿֿ


האם יש לי איזו הצעה קונקרטית, או שסתם כתבתי פוסט מבאס?

האם אני מציע לוותר על ״איכות קוד״ כערך עליון?

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

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


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



אז מה, בכל זאת, עושים?


טוב. אני מקווה שהצלחתי להעביר את הדילמה.

גישה אחת היא להמשיך בדרך המקובלת יותר בתעשייה - הצבת איכות כערך עליון של הארגון.

גישה נוספת שאני רוצה להציע היא ניהול דינאמי של מצב איכות הקוד:
  1. ננהל רשימה "חיה" של היבטים לא אופטימליים בקוד. את הרשימה כדאי לאסוף ממגוון חברים בארגון.
    1. אם תנהלו את הרשימה הזו ברצינות - מהר מאוד תגיעו לעשרות פריטים ברשימה, ויותר.
    2. אם לא הגעתם - סימן שלא חיפשתם מספיק טוב. זה כמו לשאול את עצמכם ״במה אני פחות טוב?״ - ולא למצוא שום דבר.
  2. מתוך הרשימה - מצאו פריטים בעלי Impact: עדיף impact עסקי, אבל גם impact טכנולוגי - פחות בעיות, יתר קלות לקוד שנוסף למערכת, בקרה טובה יותר על המערכת וכו׳.
    1. סביר שייקח לכם כמה סיבובים על מנת להבין היכן באמת נמצא ה Impact - רעיונות עלולים להישמע טוב, אך להיות חסרי impact לחלוטין בפועל.
  3. תזמנו זמן עבודה מוגדר לצורך שיפורים יזומים ופתרון הפריטים החשובים ביותר ברשימה. ה benchmark הבריא שאני מכיר הוא כ 20% מזמן העבודה
    1. פרשנויות שונות מה עושים בזמן הזה (כמו: ״באגים הם ברשימה״) - יכולים להפוך אותו ללא רלוונטי.
  4. שווה לעבוד בצעדים קטנים. אם יש בעיה גדולה, הרשו לעצמכם למצמצם אותה ולראות מה ההשפעה העסקית שנובעת מכך. למשל: תהליך ה deploy אורך 15 דקות? נסו לבצע שיפור (נניח: המקצר אותו ל8 דק') ולראות מה ההשפעה בפועל. זה יותר טוב מלהשקיע פי כמה עבודה בכדי להביא אותו ל 2 דקות - ולגלות שה impact לא כ"כ משמעותי.
  5. חשוב לערב את אנשי הצוות בהחלטות ובעשיה. 
    1. זה לא רק עניין של engagement ושותפות-גורל. זה גם עניין של ״חלונות שבורים״: לחדד ולהזכיר לכולם שקוד טוב הוא ערך חשוב - ושאנחנו כן משקיעים בו. ושהשקעה בו נושאת פרי.


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

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

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


הנה כמה פריטים מרשימות כאלו שניהלתי - שאכן עשו impact:
  • פירוק של טבלה גדולה ב DB לכמה טבלאות - ע״פ דפוסי שימוש שונים.
  • העברת נתונים מבסיס הנתונים ל Redis.
  • שינוי הייצוג של הנתונים - למבנה קל יותר לעבודה.
  • פירוק שרת Redis לשני שרתים ע״פ דפוס שימוש: כזה ל cache וכזה לנתונים שחשוב שיישמרו. הפירוק אפשר לנו לנקות את ה caches ביעילות מבלי גרימת נזק.
  • כתיבת מודול או microservice מחדש. צריכות להיות סיבות טובות למדי - לכתיבה מחדש (ופעמים רבות - יש כאלו).
  • שינוי מבני של flow מורכב במערכת.
  • ניקוי קוד ישן / נתונים ישנים מבסיס הנתונים.
  • הוספת כלי ניטור על מצבים חשובים במערכת - שחסרה נראות שלהם (דברים מאוד ספציפיים)
  • Key Security Items
  • Key Performance Items
  • סידור נתונים ל BI
ברור שהיה עדיף למנוע מראש את היווצרות של המצבים הללו. בהינתן הדינמיקה הארגונית / הלחצים / וחוסר ידיעת העתיד - עד כמה זה באמת אפשרי?
ברור שזו רשימה ספציפית בהקשר ספציפי - שלא אוכל להעביר בפוסט.


הנה כמה פריטים שהיו ברשימות שכאלה, ולעולם לא הגיעו לידי מימוש:
  • סידור flows מסוימים בקוד בכדי שיהיו פשוטים וברורים יותר (הרבה מקרים).
  • HTTP being used internally (ולא https)
  • מקרים מסוימים בהם כשל במיקרו-שירות אחד - גורר כשל במיקרו-שירות אחר
  • Schemas שונים בבסיס הנתונים שנמצאים ב encodings שונים: אחד בשוודית והשני ב ISO.
  • ניקוי ה git repository והסרת 25MB מיותרים.
  • Various Security Items
  • Various Performance Items
  • סידור נתונים ל BI
  • וכו׳
האם הפריטים הללו ראויים לרשימה?!
בוודאי שכן!:
  • הם מהווים נקודת reference חשובה - מה יותר חשוב ממה. מה יגרום ליותר impact ממה. את הרשימה נרצה כל הזמן למיין כאשר הפריטים בעלי ה impact הגבוה ביותר נמצאים בראש - ועוברים תדיר לעבודה.
    • נ.ב.: עדיף פריטים קטנים שנכנסים כל הזמן לעבודה - מפריטים גדולים שנכנסים לעבודה רק פעם בחצי-שנה או רבעון. זהו מסר חינוכי ומתמרץ, וגם הזדמנות להכניס יותר שיפורים משמעותיים.
  • עצם מעבר על הרשימה עם הצוות יוצר את ההבנה שאלו דברים לא טובים, שיש להימנע מהם בעתיד. יש פה למידה, ובניית קונצנזוס - דברים חשובים גם כן.
  • עם הזמן, בעיות מחמירות / גדלות בחשיבות - ואז פריטים שהיו בתחתית הרשימה - עוברים לראשה. חשוב לעקוב ולהבין את הבעיות על מנת לאתר אותן ולטפל בהן מספיק מוקדם.
    • בכלל, יצירת בסיס-ידע של בעיות במערכת שקרו ואפשר ללמוד מהן - הוא דבר שימושי. מה שקורה במערכת שלנו והוכח כבעייתי - הוא לקח הרבה יותר חזק ומשמעותי ממקרים תאורטיים, המתוארים בספרות כתובה כלשהי.





סיכום


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

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

אני זוכר את התקופה שעבדתי ב SAP והיינו פותחים Critical Bug (הרמה הגבוהה ביותר) על כל מקרה בקוד בו עשו [3] catch Throwable (מעל ה JVM). היום אני מתעלם ממקרים כאלו - ואפילו כותב catch throwable בעצמי - אם זו הצורה המקובלת לתפוס Exception בבסיס הקוד בו אני עובד.

היה לנו איזה כלי בשם Sonar (ניתוח סטטי של קוד) שניסה לחשב את ה Technical Debt של בסיס-קוד שהוא סרק בצורה מספרית / דולרית. לכל Catch Throwable הוא נתן תג מחיר של  כמה עשרות דולרים (לא זוכר בדיוק). יכולתי לפתוח tickets חסרי impact לחלוטין, ברמת הדחיפות הכי גבוהה - ולהרגיש טוב עם עצמי שאני חוסך כסף לחברה. צעירות!


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


---



[2] על בסיס מדדים כמו cyclomatic complexity או מספר הפרמטרים המועברים לפונקציה.

[3] למרות שהייתה כוונה ברמת ה JVM יום אחד לעשות שימוש ב Throwable למטרה מעט אחרת - כבר עברו 23 שנה, ולא נעשה בו כל שימוש. כ״כ הרבה מפתחים השתמשו כבר ב Throwable כאילו הוא Exception לכל דבר - כך שכבר לא נראה לי שניתן לייחס לו בגרסה עתידית משמעות אחרת.