2020-05-30

איך לנצח את הסיבוכיות?

בתואר שני במדעי המחשב, היה קורס חובה בשם ״סיבוכיות״. עסקו בו במושגים כמו DTime, NP-Hard, NP-Complete או PSPACE. בעצם, סיווגו בעיות קשות מדי לקטוגריות ואבחנו אבחנות שונות לגביהן.

זו בהחלט גישה של מדעני תוכנה. תאורטית.

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

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

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

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

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

בתוכנה יש שני סוגי סיבוכיות: סיבוכיות נחוצה (essential complexity) וסיבוכיות מקרית (accidental complexity). את הראשונה ראוי למתן (אפרט בהמשך) ואת השנייה יש להכחיד.

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


זיהוי סיבוכיות


שלב ראשון בטיפול בסיבוכיות - הוא הזיהוי שלה. 

סיבוכיות היא כל מה שמקשה על הבנה או שינוי של קוד.

  • אלגוריתם מורכב = סיבוכיות
  • ריבוי תלויות = סיבוכיות
  • קוד המפוזר באזורים שונים במערכת = סיבוכיות
  • קוד בלתי צפוי (פונקציות ״calc״ שמעדכנת ערך של האובייקט בבסיס הנתונים) = סיבוכיות.
  • שימוש בסגנון / קונבנציות / ספריות לא מקובלות = סיבוכיות
  • יישום Design Pattern מדהים היכן שאינו באמת נדרש = סיבוכיות
  • וכדומה....

קוד שקשה (/מסוכן) לשנות אותו - הוא מסובך. קוד שקל לשנות אותו - הוא פשוט.

שאלה: כיצד ניתן להוסיף קוד, לקוד מסובך - ולפשט אותו?

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

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

כלים, כמו IDE, עוזרים לפשט את הקוד (במעט).



העיקרון האובייקטיבי לסיבוכיות של קוד

אם דיי אנשים חושבים שהקוד שלכם הוא מסובך - אז הוא מסובך.

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

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

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

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

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

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

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

Code Review ו/או Code Inspection הם תהליכים יעילים לאיתור סיבוכיות בקוד.


זיהוי סיבוכיות בקוד לאחר מעשה

לפעמים אנו מבינים סיבוכיות של קוד רק תוך כדי עבודה על שינוי:
  • שינוי קונספטואלי אחד (״מעתה הרשה לפעולה להתרחש 3 פעמים, ולא רק 2״) - דורש שינויים במספר (גדול) של מקומות בקוד. 
    • השאיפה תמיד בקוד היא ששינוי קונספטואלי אחד - ידרוש שינוי מאזור ממוקד אחד בקוד.
  • Design Weight - הקוד גורם לנו לעומס קוגניטיבי ומאלץ אותנו זכור ולקשר נקודות, מידע שקשה ״להחזיק״ בראש מבלי לאבד פרטים ולהזדקק לחזור ולקרוא בקוד. קוד מסובך הוא כמו ספר בפיסיקה, שכל פעם צריך לחזור לפרק הקודם ולהיזכר ולהבין מחדש במה בדיוק מדובר. קוד פשוט הוא כמו עיתון שניתן בכל רגע לקפוץ לכל עמוד ופסקה - ובקלות להבין במה מדובר.
  • תוך כדי שינוי שנראה פשוט בתחילה, אנו מגלים עוד ועוד נקודות שיש לקחת בחשבון. לעולם איננו יודעים בבטחון אם סיימנו 50% עבודה או רק 10% עבודה. יותר גרוע: כאשר ככל שאנחנו מתקדמים, הבטחון שלנו מתי וכיצד יראה הסוף - הולכים ומתרחקים.

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

בספר “A Philosophy of Software Design”, שהוא הטריגר לפוסט (מזמן רציתי לכתוב על הנושא, אבל הייתי זקוק לטריגר שכזה) - וגם חלק מהפוסט מבוסס עליו - מאפיינים דמות בשם ״הוריקן טקטי״ (לקחתי לי חופש תרגום מ Tactical Tornado) של מפתח החולף על פני המערכת, תוך כדי שהוא מספק פיצ׳רים במהירות, אבל פיצ׳רים שסגורים רק ב 80% - ובסופו של דבר דורשים עוד עבודה ו/או מסבכים את המערכת. המנהלים לעתים רואים אותם כגיבורים, ״למה אין לנו עוד מפתחים שמספקים תוצאות כל-כך מהר?!״ - אבל המפתחים שנתקלים בקוד, ועוקבים אחרי הדינמיקה שמתרחשת בפועל - מבינים במה מדובר. 

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

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



אז מה עושים?


זה החלק הקשה. חפשו קצת באינטרנט - בטח תמצאו. יאללה ביי!

(זה מה שמתחשק לי לומר, אבל מרגיש לי קצת לא אחראי)

----




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

קונבנציות, Linting, תהליך Design Review, דפוסי עיצוב או SOLID, וכו׳.

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

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




צמצום שימוש בכלים מתקדמים

הורשה, Threads / co-routines, Generics, ועוד - הם כולם כלים ראויים שיש להם מקום במערכות פשוטות. עדיף לנסות להשתמש בהם בשימוש הבסיסי ביותר: הורשה בעומק של 1, Generics של פרמטר אחד, וכו׳. 
פעמים רבות אנשים נוטים להאמין שהקוד יהיה טוב יותר אם נשתמש יותר ב״כלים מתקדמים״, וכך יש שרשראות הורשה ארוכות, Thread המקצרים זמנים בלתי-מדידים (מרוב שהם קטנים), או תסבוכת של טיפוסי Generics.

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

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


פיזור הסיבוכיות

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


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

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

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


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


הוא עדיין לא פשוט לטעמי, אבל הרבה יותר נשלט / נסבל. הוא ראוי לעוד סיבוב של פישוט.

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

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



הכמסה של סיבוכיות 

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

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

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

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


System Decomposition

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

מתוך הספר A Philosophy of Software Design. תודה לתומר רוטשילד שהכיר לי את המטאפורה היפה הזו, ואת הספר.

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

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

הנה פונקציה לדוגמה שלא מוסיפה הרבה ערך:

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

דוגמה המופיעה בספר היא ממשק קריאת הקובץ בגירסאות המוקדמות של ג׳אווה:


האם מישהו מכם זוכר שהוא אי פעם נדרש לפתוח קובץ ללא BufferedInputStream? או הרכיב Streams אחרים?

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

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

דווקא ביוניקס, יש ממשק עמוק לגישה לקבצים: הפונקציה ()open מאפשרת בפעולה אחת לפתוח קובץ, מתוך path מורכב שניתן לתאר בשורה אחת, ויש optional flags בכדי לשלוט בכמה חלופות שימושיות (למשל: לפתוח קובץ לקריאה בלבד).

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

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

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

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


סיכום


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

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

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



לינקים רלוונטיים

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

Defining Errors Out Of Existence - פוסט של תומר רוטשילד, על רעיון נוסף מהספר ״A Philosophy of Software Design״








2020-05-23

על מובילי-דעה (gurus) בעולם התוכנה

שואלים אותי לפעמים חבר׳ה צעירים מהיכן אפשר להכיר וללמוד על מובילי דיעה (״גורו״ בפי העם) בעולם הנדסת התוכנה. 

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

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

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

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



הרשימה שלי, ביום שישי כלשהו בחודש מאי


הרשימה הראשית:
  • מרטין פאוולר (Martin Fowler)
  • הדוד בוב (Uncle Bob aka Robert C. Martin) -- גם מגדיר ה SOLID וה Clean Code
  • קנט בק (Kent Beck) -- גם מיוצרי ה Wiki, גם מכותבי JUnit, גם TDD, ומה לא...
  • David Heinemeier Hansson - DHH -- גם מיוצרי ה Ruby On Rails
  • יואל ספולסקי (Joel Spolsky) -- גם אחד היוצרים של StackOverflow
  • אנדי האנט (Andy Hunt) -- גם ה "Pragmatic Programmer״
  • לינוס טורבאלדס (Linus Torvalds) -- גם יוצר הליבה של לינוקס
לא כללתי ברשימה, בכוונה, מובילי-דעה בטכנולוגיות ספציפיות (ג׳אווה, nodeJS, ראקט, גו, וכו׳) - יש כמה וכמה כאלו לכל טכנולוגיה, אך הם כמעט חסרי-משמעות למי שלא חלק מקהילת המשתמשים של הטכנולוגיה. DHH מעניין לכולם, לדעתי.

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

לא כללתי ברשימה, בכוונה, ״כינורות שניים״: לכמה מהדמויות פה יש כמה שותפים קרובים. למשל Dave Thomas הוא לא פחות ה ״Pargmatic Programmer״ מאנדי - אולי אפילו יותר, אבל אנדי יותר מעניין לדעתי. Jeff Atwood הוא היוצר השני של StackOverflow וגם פעיל ומעניין - אבל פחות מיואל, לטעמי.

לא כללתי ברשימה עוד כמה וכמה שמות שעלו לי בזמן הכתיבה. העדפתי לקצר ולהתמקד בעיקר.


רשימת ה one-trick pony:
  • אריק ריס (Eric Ries) -- מקים תנועת ה "Lean Startup״
  • ג׳ז ״הצנוע״ (Jez Humble) -- מחבר ה Continuous Delivery
  • אריק אוונס (Eric Evans) -- מוביל תנועת ה DDD
  • מייקל ניגארד (Michael Nygard) -- פשוט פרסם כמה דברים טובים! מחבר הספר "!Relase it"
אני מקווה שהכותרת אינה בוטה מדי.
אלו אנשים סופר-מעניינים שכדאי להכיר, אבל כשרציתי להמשיך עם הכתבים / חומרים / הרצאות שלהם - ראיתי שאותם רעיונות חוזרים שוב ושוב בוריאציות שונות. זה לא מוריד מחשיבותם, רק אומר שאחרי שספגתם את עיקרי הדברים - אין הרבה טעם להמשיך ולעקוב אחריהם. לפחות זה הניסיון שלי.


הדור הקודם:
  • אריך גמא (Erich Gamma) -- מהרביעיה שהגדירה את ה Design Pattern. כתב עם קנט בק את JUnit.
  • קרייג לרמן (Craig Larman) -- היה מקביל לדוד בוב לתקופה, אך הדוד בוב קטף את כל התהילה
  • פרדריך ברוקס (Fred Brooks) -- מחבר ה Mythical Man-Month
  • דיוויד פרנס (David Lorge Parnas) -- חשוב כמו פרדריך ברוקס - אבל פחות מוכר כיום.
  • אליסטר קוברן (Alistair Cockburn) וסקוט אמבלר (Scott Ambler) -- לא היו באמת צמד, אבל רק באיחוד כוחות אני מרגיש נוח להכניס אותם לרשימה החשובה הזו.
לו היינו מחברים את הרשימה בשנת 1999 - החברים בקטגוריה הזו היו במרכז הרשימה הראשית.
בשנת 2020, הרעיונות שלהם עדיין משפיעים - אבל צריך להתאמץ יותר בכדי להגיע ולהתחבר לתכנים המקוריים שלהם. רק למי שמעוניין.

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

    אני מסתכל על הרשימות, ואין דמות נשית ברשימות. בכלל.
    צר לי על כך, אבל באמת יש פחות ״מובילות״ דעה בתחום. בכדי מעט לאזן, אציין את יוהנה רוטמן (Johanna Rothman) שהספר שלה ״Behind Closed Doors״ פתח לי כמה ״דלתות״ בחשיבה, ואת טרישה ג׳י (Trisha Gee) שמופיעה יותר ויותר בכנסים בשנים האחרונות, וגם עשתה כמה Keynotes בכנסים חשובים. היא מדברת לעניין, בלי הסחות דעת וססמאות. יש גם דוברת ישראלית בשם חן שפירא (Gwen Shapira) שהגיעה לכמה פורומים מכובדים.


    מוביל דעה: מרטין פאוולר (Martin Fowler)




    זה הבחור שהשפיע עלי אישית, במידה הרבה ביותר.

    איזו חותם הוא השאיר?

    מרטין טבע כמה מהמונחים המוכרים והנפוצים בעולם התוכנה: 
    • Refactoring
    • Dependency Injection
    • (Domain Specific Languages (DSL
    • ועוד...
    מרטין חיבר כמה מהספרים החשובים בעולם התוכנה שנכתבו בשני העשורים האחרונים:


    אציין כמה מהספרים החשובים ביותר:
    • Refactoring - ספר שרובו טכני עד שעמום, אבל הפך לפרקטיקה שגורה וחשובה של כל איש תוכנה. יש גם גרסאת רובי (שפה שמובילי דעה רבים עברו דרכה)
    • Patterns of Enterprise Application Patterns - דפוסי עיצוב של מערכות מידע / מערכות ווב. ספר חשוב ורלוונטי.
    • UML Distilled היה ספר פורץ דרך וחשוב בזמנו. מרטין טען שבמקום מדריכים ארוכים ומורכבים על UML שרובם היו 500-1000 עמודים ולא ממצים - הוא יכול לזקק את העניין לספר ממצא בן 100 וקצת עמודים. הוא עשה את זה - ו״הוריד את ה UML אל העם״. שפת ה UML כבר לא חשובה כל-כך - אבל זה היה תרגיל מרשים בפישוט והנגשת מורכבות.
    • Analysis Patterns הוא ספר חשוב מאוד (ולא קל לעיכול) על מידול של ביזנס לתוכנה. כעשור בערך מרטין עבר על רוויזיה של הספר שתהיה קלה, מודרנית ושלמה - אך מעולם לא סיים את העבודה. כלומר - יש רק ספר ישן וחצי אפוי - אבל ספר משמעותי מאוד.
    מרטין חתום על סדרה של ספרים של מחברים אחרים, שהוא נתן להם את החותם שלו ("Addison wesely martin fowler signature books״). 
    המצליח בספרים הללו הוא Enterprise Integration Patterns של בחור בשם גרגור הופ - שגם הוא מוביל דעה בעצמו.

    מרטין גם מרצה המון בכנסים. נראה לי שכל חודש הוא מופיע בכמה כנסים. הוא אורח כבוד קבוע ב QCon ו ;Goto - שני כנסים חשובים מאוד.

    מרטין הוא אחד מהיוזמים והחותמים של ה Agile Manifesto. הוא הבחור עם הקרחת שמופיע ליד הלוח בתמונה המטושטשת.

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


    מה מייחד את מרטין פאוולר?

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

    כש IoC (קיצור של Inversion of Control) היה דפוס שהחל לתפוס תאוצה בחזית התעשייה, מרטין הבין שיש כאן משהו שימושי לקהל רחב בהרבה - אם כי מורכב להבנה. מרטין טבע את המונח Dependency Injection - ובעצם הצליח לפשט את ההצגה של הרעיון ולהנגיש את היישום שלו לקהל רחב בהרבה.

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

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

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

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


    מה מרטין עושה היום? איפה הוא עובד?

    מרטין הוא ה״מדען הראשי״ של חברת ייעוץ תוכנה בשם ThoughtWorks. החברה הזו נחשבת (לפחות בעבר, אולי גם כיום) לחברה מובילה בתחומה. הם יצרו את CruiseControl (לפני שהגיע Hudson/Jenkins והחליף אותו) וכאשר מייקרוסופט רצו review לפרוייקט השאפתני שלהם, בשם NET. - חברת ThoughtWorks היו ה reviwer.
    לא שמעתי דעות על ThoughtWorks כבר שנים, אני יודע שהם גדלו מאוד - ומקווה בשבילם שהצליחו לשמור חלק מהאיכויות שפרסמו אותם בתחילת הדרך.


    היכן אפשר למצוא תכנים ורעיונות חדשים של מרטין?

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

    אפשר לחפש הרצאות שלו ביוטויב. אין לו ערוץ משלו, והרצאות שלו מתפרסמות ע״י חברות שונות וכנסים שונים - שנותנים את החסות, אבל בד״כ הוא מפרסם 2-3 הרצאות משמעותיות בכל שנה.

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



    סיכום


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

    ניסיתי להרחיב מעט על אחד ממובילי-הדעה הללו, החביב עלי - מרטין פאוולר.

    כתבו לי אם אתם חושבים שזה שימושי / מעניין / רלוונטי - ואולי אמשיך לכתוב על עוד כמה מובילי-דעה. אפילו כתבו לי על מי.

    יודעים מה, אם אני מקבל 100 לייקים באינסטגרם - אני מבטיח פוסט נוסף! 😉

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



    2020-05-16

    הפילוסופיה של יוניקס

    ראשית כל, בואו ונשאל: למה שתעניין אותנו הפילוסופיה של יוניקס? - פילוסופיה של חבורת אנשים מזוקנים משנות ה 60 וה 70 של המאה הקודמת?!

    כלומר: אלוהים ישמור! למה שנתעניין במשהו בן 50 שנה? הרי אנחנו בתרבות שמקדשת את החדש-ביותר. כל מה שקרה לפני הקורונה - נראה היום כבר רחוק וכמעט לא-רלוונטי.

    אפשר לטעון שיוניקס היא מערכת ההפעלה הטובה ביותר (הנדסית) שנכתבה אי-פעם. אפשר לטעון שהפילוסופיה של יוניקס הכילה את עקרונות ה SOLID, מיקרו-שירותים, Lean, ו Devops culture כבר בשנות ה-70, רעיונות שעדיין פופולריים גם היום - מה שעשוי בהחלט להקל עלינו את ההתעניינות בפילוסופיה הזו.

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

    אז מה היא בדיוק הפילוסופיה של יוניקס?

    - בפילוסופיה, כמו בפילוסופיה - אין דבר כזה שאפשר לתמצת במשפט אחד.



    קיצור (אגרסיבי) של הפילוסופיה של יוניקס


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

    בכל מקרה, ממגוון הגרסאות, יש שני כללים שבולטים וזכורים יותר מכולם. נבחן אותם אחד אחרי השני:
    Make each program do one thing well. To do a new job, build afresh rather than complicate old programs by adding new “features.”

    מילות המפתח שצצות כאן הן: מודולריות, פשטות, Single Responsibility, העקרון הפתוח-סגור, ו Lean.

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

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

    אבל....

    מכיוון שכל אפליקציה היא קטנה פשוטה ועושה בדיוק דבר אחד - לא ניתן לבנות פונקציוליות עשירה ומורכבת, ללא הכלל השני:
    Expect the output of every program to become the input to another, as yet unknown, program.
    פונקציונליות עשירה יותר, מורכבת ע״י ״הרכבה״ של אפליקציות פשוטות בזו על זו.

    cat foo.txt | wc -l

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

    אנו משתמשים מכאן ב Pipe (אם אתם לא יודעים מה זה pipe - לכו מיד וקראו!), על מנת להעביר את הפלט לאפליקציה אחרת, wc (קיצור של word count [א]) ורק היא תשלח את התוצאה שלה ל standard output.

    תכנון ה pipe ע״י Doug McIlroy בשנת 1964. קצר, פשוט, ענייני.


    wc מקבלת פרמטרים (במקרה שלנו l-) המאפשרים לה לפעול טיפה אחרת, ובעצם לספור שורות.

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

    ה Pipe מאפשר הרכבה גמישה בין מרחב גדול כל-כך של אפליציות, ומאפשר ל Shell של יוניקס לבצע מגוון אדיר של פעולות פשוטות ומורכבות. ההצלחה של ה Pipe נובעת מתוך הגדרה של ממשק כללי ופשוט להחריד:
    • שטף של בתים (לא ביטים), בד״כ ב ASCII. כלומר: טקסט.
    • אפשר שיהיה לו סוף - בדמות EOF.
    • n\ סימן להפרדה בין רשומות (או ״שורות״)
    • +[t\ ] (כמות כלשהי של טאבים או רווחים) - הפרדה אפשרית בין שדות.

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

    למשל bat, אפליקציה הדומה ל cat (הנה עוד דוגמה כיצד שמות לא מוצלחים מדרדרים אותנו. מכיוון ש cat נשמע כמו חתול, בחרו בחיה אחרת ויצרו שם שלא מסביר את עצמו.) המאפשרת syntax highlighting של קוד, כבר עובדת הרבה פחות טוב.

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

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

    פשטות אמיתית דורשת המון משמעת, כמו שנאמר: ״Simplicity is the ultimate sophistication״.



    סיכום 


    אז מה היא הפילוסופיה של יוניקס, ולמה היא חשובה?

    הפילוסופיה מקדמת, מיישמת, ומדגימה סדרה ארוכה של עקרונות חשובים בהנדסת תוכנה. זה כבר ערך בפני עצמו. קראו את הפסקה הבאה:
    ״Build a prototype as soon as possible. Most users looking for a new piece of software won't know what they really want until they see it, so requirements documents are often misleading about the users' real needs. The Unix design philosophy makes prototyping a central part of the methodology: give the user something, anything, up-front to criticise, and work from there.״
    אתם מאמינים שהיא נכתבה במיליניום הקודם? אז מי המציא את ה Lean-Startup? אריק ריס או מפתחי היוניקס?!

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

    למשל: כל מודל הרכיבים (components) של RactJS או +Angular2 - מרגיש כמו שידור חוזר של הפילוסופיה של יוניקס: רכיבים פשוטים, לשימוש חוזר, שניתן לזרוק בקלות אם הם כבר לא רלוונטיים, וממשק פשוט ככל האפשר.

    לפני כעשור ועוד עבדתי עם מודלים אחרים של רכיבי UI, בעיקר Java Portlets או כל מיני תקנים של widgets ו gadgets אחרים. הממשק היה כמעט תמיד מורכב בהרבה - והקשה מאוד על קהילה רחבה לתרום למודלים הללו. זה לא היה פשוט.

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

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


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


    ----

    [א] הערת אגב: עם הכבוד הרב שאני רוחש לפילוסופיה של יוניקס - אני עדיין מאמין ששמות מאוד קצרים הם טעות גדולה של יוניקס שמחירה מודגם שוב ושוב. שמות מאוד קצרים קשה יותר לזכור, וקל מאוד לבלבל בינהם / לפספס את המשמעות.
    יוניקס הייתה טיפה מוצלחת יותר אם ל cat היו קוראים concat, ול wc היו קוראים wordcount (או פשוט count - מכיוון שיש לה הרבה אופציות ספירה שונות), ול man היו קוראים manual. הקנאות לקיצור הרגה את הקריאות ביוניקס.


    ----

    לינקים רלוונטיים

    סיכום החוקים של הפילוסופיה של יוניקס. אפשר לקרוא ולהנות.

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