מחלקות ב javaScript

תלמיד: מאסטר, כיצד כותבים מחלקה Class בג׳אווהסקריפט?
מאסטר: ג׳אווהסקריפט היא דינמית, כחומר ביד היוצר – עליך להגדיר בעצמך כיצד נראה Class.
תלמיד: בעצמי? אין איזו המלצה מקובלת? Best Practice? Pattern?
מאסטר: יש הרבה. מאסטר רזיג אוהב את אופצייה #1, מאסטר כץ אוהב את אופציה #3. אם יש לך מערכת שדורשת אלף Classes, בג׳אווהסקריפט אתה יכול להגדיר כל Class במערכת בצורה ייחודית.
תלמיד: כל Class בצורה ייחודית?? כיצד ניתן לתחזק קוד שכזה? כיצד אפשר בכלל לקרוא אותו?
מאסטר: אל תהיה טיפש. זה שהשתמשתי באמירה כלשהי לא אומר שאתה באמת צריך לרוץ לעשות את זה.
תלמיד: הא…שייך לסדרה מבוא מואץ ל JavaScript ו jQuery

מוטיבציה
בג׳אווהסקריפט אין מחלקות (Classes בג'אווה / NET.), אולם רוב המפתחים המקצועיים בג׳אווהסקריפט מדמים מחלקות. לא ניתן להאשי אותם: מחלקות הן דבר שימושי.

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

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

ישנן עשרות צורות אפשריות לתאר מחלקות בג׳אווהסקריפט. ניתן לחלק אותן בבירור ל-2 משפחות:

  • מחלקות בעלות אכיפה נוקשה של הכמסה (מבוססות closure).
  • מחלקות יעילות מבחינת צריכת זיכרון ואפשרויות הורשה (מבוססות prototype).

שילוב אלגנטי שנותן ״גם וגם״ – עדיין לא ראיתי.

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

הנה המחלקה בשפת Java אותה ארצה לדמות בג'אווהסקריפט:

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

משפחה א׳ – אכיפה נוקשה של הכמסה.

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

תיאור זה של מחלקה הוא ידוע מאוד ונקרא ״revealing module״, המבנה הבסיסי אמור להיות מוכר – עסקנו בו בפוסט הקודם בסדרה. זו גם הצורה בה פרויקט jQuery ממליץ לכתוב מחלקות.
יצירת instance חדש נעשה ע"י קריאה ל Factory Function (המקבילה הג'אווהסקריפטית ל Factory Method). בכדי "לסמן" את ה Factory Function – אני ממליץ לקרוא לה createXXX.

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

הנה צורה אחרת, פחות נפוצה. כל member שמוגדר עם "this" הוא ציבורי וכל member שמוגדר עם var הוא פרטי.
אני אישית מחבב אותה יותר: הקוד קצר יותר. הניראות (visibility) מצוינת באותו המקום בו מוגדר ה member/method כך שבמבט חטוף אני יכול לדעת מה private ומה לא (כמו בג׳אווה – המלים השמורות private ו public). אני לא צריך לגלול לתחתית המחלקה על מנת לדעת מהי הניראות, כמו בדוגמה הקודמת
.
בעת יצירת instance, יש לזכור להשתמש במילה השמורה new. הקונבנציה המקובלת היא לקרוא לבנאים באות ראשונה גדולה (Capital Letter). זהו חיסרון מסוים יחסית לצורה הקודמת, מכיוון שיש להיות מודעים לצורה בה המילה השמורה this עובדת בג'אווהסקריפט. פוסט המשך בסדרה מסביר את הנושא לעומק.

סכנה מוחשית במבנה זה הוא החלפה של הנראות private public. מכיוון שגם הגישה למתודה/משתנה היא שונה ע"פ הנראות שלו (this.xx או xx) אזי סביר שבעת שינוי הנראות נשכח לתקן חלק מהקריאות לצורה המעודכנת – ונשבור את הקוד. אני ממליץ להשתמש בצורה זו תחת הכלל: כל המשתנים – פרטיים, כל הפונקציות – ציבוריות. מבנה כזה שימושי עבור Data Objects, Mock Objects וכו'.

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

תיאור זה של מחלקה הוא נפוץ מאוד, הוא קרוי לעתים Prototype Pattern. אתם אמורים לזהות את המבנה הבסיסי מהפוסט הקודם בסדרה. בעצם זו וריאציה קלה על ה "Pattern הרשמי" מכיוון שהיא כוללת "סימון" של private members – בעזרת התחילית "_" (קו תחתון).  סימון זה הוא קונבנציה בלבד – ועל המפתחים לשמור על משמעת אישית על מנת לא-להשתמש ב private members מחוץ למחלקה.

תיאור זה הוא הדרך שבה CoffeeScript בחרה לתאר מחלקות. הוא מאגד את כל המחלקה בבלוק אחד (נדיר למשפחה ב') ונפטר מה object literal notation להגדרת הפונקציות.
החיסרון? המבנה מורכב מעט יותר: יש שימוש גם ב closure וגם ב Constructor.

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

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

הערות? מחשבות? – אשמח לשמוע!

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

JavaScript's This – למפתחי ג'אווה ו #C מנוסים

"מפתח JavaScript הופך ממתכנת ממוצע למתכנת מקצועי ביום שהוא מבין את המילה השמורה this".כמפתחי ג'אווה או #C אתם אמורים להישאר כרגע פעורי פה: האם ייתכן ש"מתכנתי ג'אווהסקריפט" לא מבינים דבר בסיסי כמו המילה השמורה this?!
אבל בג'אווהסקריפט, כמו בג'אווהסקריפט – יש הפתעות במקומות הלא צפויים, ו this איננה יוצאת דופן.

חלק גדול ממפתחי הג'אווהסקריפט הם מפתחי צד שרת (Java, Ruby, #C) אשר משתמשים בה "לכתוב קצת UI", סוג של "מפתחים מזדמנים".
אתם לא באמת צריכים להבין את כל הפינות של ג'אווהסקריפט על מנת לפתח בווב. עבור רוב עבודות הג'אווהסקריפט, תוכלו להסתדר עם "להבין מספיק".
אם אתם עושים עושים בג'אווהסקריפט קצת מעבר – להבין את this בהחלט יכול לעזור.

שייך לסדרה מבוא מואץ ל JavaScript ו jQuery

תשכחו את מה שידעתם בג'אווה / #C…

Scope בג'אווה כפי שאתם מכירים אותו. פשוט וברור.

בג'אווה, להבין את ה scope זה דיי פשוט: scope נוצר ע"י סוגריים מסולסלים.
אם אני רוצה לדעת מיהו המשתנה n – פשוט אטייל בעין, scope אחר scope עד שאמצא היכן הוא הוגדר.
אם אני רוצה לדעת מיהו this – פשוט אטייל בעין, scope אחר scope עד למחלקה הראשונה שאני רואה. כמעט תמיד תהיה רק מחלקה יחידה (קינון מחלקות הוא דיי נדיר בג'אווה).

בג'אווהסקריפט פיצלו את ה scope, כפי שקיים בג'אווה, לשניים:

  • Scope להגדרת משתנים, המוגדר ע"י פונקציה (ולא סוגריים מסולסלים! [א])
  • Context[ב] להגדרת this, המוגדר ע"י שייכות קונקרטית לאובייקט – בזמן ריצה. הסבר בהמשך.
הכללים לקביעת ה-context הם שונים מהכללים לקביעת ה scope. עצם ההבנה שהחוקים למציאת ה scope הם לא דומים לחוקים למציאת ה context – היא מחצית הדרך להבנה של this.

כיצד נוצר Context?

כלל #1: כאשר פונקציה (function) שאינה משויכת ישירות לאובייקט נקראתה context יהיה האובייקט הגלובלי. כלומר this יצביע לאובייקט הגלובלי. האובייקט הגלובלי הוא האובייקט עליו נרשמו כל המשתנים / פונקציות שהוגדרו ללא var או ללא שייכות לאובייקט כלשהו. בדפדפנים, האובייקט window (שמתאר Tab של דפדפן) נבחר לייצג את האובייקט הגלובלי. כל השמה "גלובלית" תתווסף עליו.

כלל #2: כאשר פונקציה המשויכת ישירות לאובייקט (מתודה method) נקראת – ה context שלה יהיה האובייקט אליו היא שויכה. כלומר, this יצביע לאובייקט שעליה היא הוגדרה, או ששויכה במהלך הריצה (דוגמאות בהמשך).

זהו זמן טוב לדוגמה.

השמשתי בצילומי מסך מה IDE האהוב עלי לכתיבת JavaScript, הרי הוא WebStorm. העטיפה "it" ומשפטי ה expect נובעים מכך שזו בדיקת-יחידה, הנעזרים בספרייה בשם Jasmine.
כל הבדיקות בפוסט זה עוברות, ולכם אתם יכולים להניח שהחלק שבתוך ה expect שווה לערך בתוך ה toBe.

ב1 אנחנו רואים שהפונקציה מחזירה את השם של האובייקט הגלובלי. בגלל שאני מריץ את הקוד בתוך "מריץ הבדיקות" השם שם האובייקט הגלובלי במקרה זה הוא "runner".

ב2 הגדרנו אותה פונקציה על אובייקט – והנה this התייחס לאובייקט עליה הוגדרה – הרי הוא A.

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

המילה השמורה new, ומה שהיא עושה ל this…
בנאי (constructor) בשפת ג'אווהסקריפט היא פונקציה רגילה לכל דבר. הקונבנציה המקובלת היא לקרוא לבנאי בשם עם אות ראשונה גדולה (capital letter). שאר הקסם קורא בעת הפעלת הפונקציה עם המילה השמורה new.

שימוש במילה השמורה new גורם לשלושה דברים:

  • נוצר אובייקט חדש ריק – השקול לכתיבת "{ }".
  • האובייקט החדש מועבר לבנאי, כפרמטר בשם this. הדברים האלו קורים מאחורי הקלעים – בקוד עצמו לא יהיה זכר ל this בחתימת הפונקציה.
  • במידה והבנאי לא סיפק ערך החזרה return (עדיף שלא יספק) – ג'אווהסקריפט מוסיפה return this ביציאה מהפונקציה.

הנה דוגמה:

במקרה זה, השתמשנו ב new על מנת לייצר אובייקט חדש.
למרות שהפונקציה ConstructorA (=בנאי) לא מוגדרת בתוך אובייקט, בזמן ההרצה this דווקא כן מתייחס לאובייקט.
מסקנה: אין לחפש את האובייקט במבנה הקוד, אלא לחפש את נקודת הקריאה (invocation) של הפונקציה ומאיזה context היא התבצעה.

הנה דוגמה בעייתית:

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

  • משתנה / פונקציה מקומית = טורקיז.
  • משתנה / פונקציה של אובייקט = חום-צהוב.
  • (משתנה / פונקציה גלובליים = סגול)

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

הבעיה נובעת מכך שהפונקציה sayMyName איננה משויכת לאובייקט שנוצר – כי אם "נתלית" על ה scope של הפונקציה ConstructorA. הנה שמה צבוע בטורקיז = משתנה לוקאלי. אם אתם זוכרים את כלל #1 – פונקציה שלא קושרה לאובייקט משויכת ל context של האובייקט הגלובלי.
בגלל שמדובר ב closure [ג] היא תישאר "בחיים" ויהיה ניתן להשתמש בה גם לאחר שהפונקציה ConstructorA סיימה לרוץ.

Webstorm מזהה את הבעיה ואכן מציג אזהרה על ה this (מת עליו). כלי ניתוח ססטי כמו JSLint/JSHint – לא מזהים את המצב הזה. כנ"ל IDEs רבים אחרים.

למי שרוצה לחפור קצת יותר: ניתן לומר ש sayMyName דומה מאוד למתודה סטטית (static) בג'אווה: היא קיימת על המחלקה ולא מכירה את האובייקט – ולכן לא ניתן לגשת ממנה אל האובייקט. בעוד בג'אווה this בתוך מתודה סטטית היה מצביע על המחלקה – בג'אווהסקריפט הוא "הולך לאיבוד" ומצביע על האובייקט הגלובלי (בשל כלל #1).

הנה הפתרון המקובל לבעיה זו:

צרו משתנה ("that") ב closure שיצביע על ה context הרצוי – ואז השתמשו בו.

השילוב של ג'אווהסקריפט ו this יכול לבלבל… 

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

הקריאה ל ()objectD.objectF.getThatName זורקת שגיאה (error), בעוד רצינו שתחזיר "E".

מה שקצת מבלבל הוא ההנחה שהקריאה this.objectE תגיע ל objectE – שהוא אובייקט אח ולא אובייקט בן.
ג'אווהסקריפט מצידה לא מודיע על בעיה: השימוש ב this.objectE מוסיף ל objectF משתנה חדש בשם objectE, שערכו undefined, ומיד אחר כך צועק על כך של undefined אין מתודה בשם getMyName. בעסה.

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

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

    getThatName: function(){ return objectD.objectE.getName(); }

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

הנה תקלה נפוצה אחרת:

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

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

ספריית jQuery מציע utility function שיכולה לעזור, proxy.$ (לחצו על התמונה על מנת להגדיל):

proxy.$ מקבלת פונקציה ואובייקט (= context) ויוצרת פונקציה חדשה הקושרת "לעד" את ההרצה של הפונקציה ל context שנבחר.

אם אתם עובדים עם דפדפנים חדשים בלבד (+IE9), אתם יכולים להשתמש בפקודת bind של שפת ג'אווהסקריפט – שעושה בדיוק את אותו הדבר. proxy$ / bind שימושית במיוחד לצורך רישום אירועים:

השוו דוגמה זו לדוגמה הקודמת – הרעיון דומה.
ב1, לחיצה על הכפתור תפעיל את draw ב context של אובייקט ה button$[ד] – שאין לו מתודה בשם draw = תקלת זמן ריצה.
ב2, לחיצה על הכפתור תפעיל את draw ב context של האובייקט objectA – היכן שהמתודה אכן נמצאת.

Call ו Apply
כלי אחרון שאני רוצה להזכיר הוא המתודות Apply ו Call, המאפשרות להריץ מתודה מסוימת ב context שנקבע באותו הרגע.

כפי שאתם רואים call מאפשר להריץ מתודה (במקרה זה – של אובייקט A) על אובייקט (במקרה זה – D), כלומר – כך ש this יתייחס לאובייקט D.
כמובן שפונקציה לעתים דורשת ארגומנטים, call מאפשרת להוסיף אותם בסדר הנכון אחרי ה context שהועבר.
ההבדל בין call ו apply הוא רק בדרך בה מעבירים ארגומנטים לפונקציה – רשימת ארגומנטים או מערך. לא משהו גדול.

בפועל השימוש ב call יכול להיות מהיר עד פי כפליים מ apply – שוני שמשמעותי רק כאשר אתם עושים מאות או אלפי הפעלות של הפונקציה.

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

——

[א] אתם יכולים לקרוא עוד קצת על הנושא בפוסט מבוא מואץ ל JavaScript עבור מפתחי Java / #C מנוסים – חלק 1, בערך באמצע.

[ב] רשמית נקרא Function Context, אבל יותר מדויק יהיה לקרוא לו Invocation Context.

[ג] ניתן לקרוא על Closure בסוף הפוסט מבוא מואץ ל JavaScript עבור מפתחי Java / #C מנוסים – חלק 1.

[ד] הכוונה ל wrapper של jQuery שעוטף את אלמנט הכפתור, אותו יצרנו בעזרת השאילתה ('.button')$.

מה הקטע של… סקראם? (ת'כלס)

בוודאי שמעתם לא מעט דיבורים על סקראם (Scrum).

  • אם עוד לא התנסתם בסקראם – ייתכן מאוד והדיבורים הללו נשמעים לא-ברורים או מופרכים.
  • אם כבר התנסתם בסקראם – ייתכן ויש פער בין הדיבורים למה שאתם מכירים בפועל.

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

שייך לסדרה: אג'ייל – מתודולוגיות פיתוח רזות

רקע
סקראם היא מתודולוגית פיתוח תוכנה המיישמת עקרונות של ייצור רזה (Lean) בעולם התוכנה (מה שנקרא אג'ייל Agile).
אם השמות מבלבלים, אתם יכולים להניח כרגע ש Agile = Lean = Scrum.

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

מתודולוגית סקראם עוסקת ב:

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

סקראם! (בספורט)

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

  • סקראם או שיטות אג'יליות אחרות (XP, Kanban)
  • כל השאר, שנקרא לו בפשט(נ)ות "מפל המים" (Waterfall).

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

במודל מפל המים מניחים שהתוכנה דומה לבניית מבנה מגורים:
  1. יש מגבלות קשיחות על סדר הפעולות (אי אפשר לבנות את הגג לפני היסודות).
  2. טעויות הן יקרות מאוד לתיקון ("העמוד הזה צריך להיות פה?!") אך תכנון מדוקדק ובקרה בביצוע יכולים לצמצמם אותן.
  3. יש חזרה רבה על פעולות ("40 דלתות, 600 חלונות, 40000 מרצפות") – כך שריכוז פעולות מייעל את הביצוע.
במפל המים קודם אוספים דרישות עבור כל המוצר, אח"כ מבצעים תכנון כללי ("ארכיטקטורה") של כל המוצר, אח"כ תכנון פרטני ("Design") של כל חלקי המוצר, אח"כ כותבים את הקוד, מבצעים אינטגרציה, בודקים היטב את כל המערכת, מבצעים תיקונים ומשחררים.
מתודולוגיות גמישות (כלומר Agile / Lean / Scrum) מניחות שבניית תוכנה היא דומה יותר לעיצוב חנות כלבו:
  1. אין לרוב מגבלות קשיחות על סדר הפעולות (אפשר לסדר את מחלקת כלי הבית לפני או אחרי מחלקת ההנעלה).
  2. יש מגוון רחב מאוד של פריטים ("פיצ'רים") – אדם אחד יתקשה לשלוט בכל הפרטים.
  3. תכנון מפורט ונוקשה מראש עלול להחטיא את המטרה. כדאי להתחיל בהדרגה, ללמוד מטעויות ולשפר את ארגון החנות עם הזמן.
  4. ריכוז פעולות יכול לייעל את העבודה, אך בצורה מוגבלת.
כמובן שהנחות אלו, שרבים יסכימו שהן מתארות את עולם התוכנה בצורה טובה יותר, מובילות למסקנות שונות לחלוטין.
כשמישהו בעל ניסיון ב"צורת עבודה רגילה" (קרי "מפל המים") נחשף לסקראם לראשונה, הוא לרוב נוטה לחפש ישר את הסדר המוכר.
אם נדמה לכם שסקראם היא "רק עבודה במחזורים קצרים + שמות תפקידים קצת שונים" – אזי אתם בחברה טובה: רוב האנשים שנחשפים לסקראם לראשונה לא מבינים את מהות הגישה השונה. זה לוקח זמן.
מה המשמעות של סקראם בפועל?
במבט של מי שרגיל ל"מפל המים", ניתן לומר שההבדלים העיקריים בדרך העבודה הסקראמית הם:

  • בסקראם אכן עובדים במחזורים (נקראים ״ספרינטים״) קצרים: שבועיים עד חמישה, כאשר בכל מחזור יש עוד טיפה פונקציונליות עובדת: "הוספת מדף מתנות במחלקת מתוקים" או "שיפור התצוגה של נעלי טיפוס הרים", בהקבלה למטפורת ההכלבו.
    במפל המים המשימות כנראה היו "מדפים (כל הכלבו)", "תאורה (כל הכלבו)" או "תצוגות מבצע (כל הכלבו)". אם הערכות הזמנים היו שגויות, היה יכול להגמר הזמן המתוכנן לביצוע הפרוייקט מבלי שיש מוצרים על המדפים.
  • בניגוד למפל המים בו יש מסמכים מפורטים כמו MRD, PRD ו Functional Spec, בסקראם מחליפים את המסמכים ברשימות מתומצתות (״backlog״) והרבה פגישות / עבודה פנים מול פנים של האנשים המעורבים. התקשורת היא ישירה, תדירה ודינמית – ולא באמצעות ניירת.
    סקראם מגדיר מספר גדול  של ישיבות שיש לקיים, כגון "Daily Stand-up", "Sprint Planning", יש גם "Sprint Sprint Demo", "Retrospective" ועוד.
  • בסקראם יש דגש על השגת יעילות (effectiveness): "כמה פ׳יצרים מועילים נוספו למערכת בפרק-זמן נתון?".
    הדרך היעילה ביותר להשיג זאת היא להפעיל שיטות לזיהוי פ׳יצרים שלא באמת זקוקים להם – ולבטלם. בסה״כ המפתחים יכתבו פחות שורות קוד, אך הם יכתבו יותר שורות קוד שלקוחות באמת משתמשים בהן.
  • סקראם מסירה סמכויות ואחריויות מהמנהלים ומטילה אותם על אנשי הצוות. אין ראש צוות שמרכז את העבודה, המעקב אחריה וההתחייבות ללקוחות. הצוות מנהל את אלה בעזרת תהליך מובנה שאמור לאפשר לצוות לעשות זאת ללא ״דמות מרכזית שלוקחת את הדברים לידיים״
  • הצוותים בסקראם הם "צוותי פ'יצר" בניגוד ל"צוותי רכיב" של מפל-המים.
    במפל המים היו צוותים כגון "צוות בסיס נתונים", "צוות UI" וצוות "לוגיקה" – צוותים הממופים לרכיבי המערכת. אם צוות ה"UI" קורס מעומס – הצוותים האחרים לא מסוגלים לעזור לו – יש להם את ההתמחויות והאחריות שלהם.
  • בסקראם כל צוות אמור להיות מסוגל לבצע "כל משימה". בצוות יש כישורים כגון בסיס נתונים, UI ולוגיקה, QA, תיעוד – כל מה שצריך על מנת לסיים משימה "מקצה לקצה".
    הנוהג הוא להימנע מלקרוא לצוות על שם רכיב במערכת ("צוות DB"), אלא להשתמש בשנות ניטרליים כמו צוותים "1,2,3" או צוותים "אדום, כחול, וירוק".
  • בסוף כל ספרינט, יש פגישה יזומה של "הפקת לקחים" על מנת לאפשר שיפורים בתהליך, שללא תשומת הלב הנוספת, לא היו מתקיימים.
אג'ייל היא לא רק סט של חוקים, כי אם פילוסופיה. פילוסופיה שניתן לקחת מחוץ לעולם התוכנה (משם היא בעצם הגיעה). הנה דוגמאות:
  • דרך חשיבה / עבודה בולטת בסקראם היא Prioritization and Time Boxing. כל פעילות (ישיבה, workshop, משימה) יש לתחום בזמן ולהתחיל מהנושא החשוב ביותר לנושא החשוב פחות. כשהזמן ייגמר, יהיו לנו כמה נושאים חשובים – שסיימנו, וכמה נושאים פחות חשובים – שכנראה נוותר עליהם. זאת במקום הרבה נושאים לא גמורים או השקעת זמן בלתי-נשלטת.
  • בניגוד לשיטת מפל המים, בה משתדלים מאוד לכתוב קוד "פעם אחת, ולא לגעת בו יותר", כשכותבים קוד בסקראם כותבים בדיוק את מה שצריך ולא טיפה יותר. סביר למדי שנחזור לקוד הזה ונבצע בו שינויים / תוספות עוד כמה פעמים. בדיקות-יחידה ו CI הם הכלים שמאפשרים לגישה כזו להיות אפשרית.
הפילוסופיה של מפל המים ("כותבים קוד פעם אחת ולא נוגעים בו יותר") היא גם הגיונית, ושימושית במקרים מסוימים גם בעת עבודה בסקראם. אני בטוח שיישום מפל המים היה שיפור משמעותי על חוסר-השיטה שקדם לה.

הנה סרטון מצחיק (אבל נכון) המסביר מהו תפקידו של ה Scrum Master

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

לסקראם יש גם כמה בעיות:

  • שוק גדול של הסמכות ויועצים – שיש להם אינטרס קודם ל"מתודולוגיית הסקראם" מאשר להצלחת הארגון שלכם.
  • כמה אלמנטים (כמו "צוות שמנהל את עצמו), פשוט לא עובדים היטב / קשים מאוד ליישום. סקראם איננה קהילה דינמית ובמשך השנים אני רואה מעט הצעות חדשות מהותית להתמודדות עם הבעיות. בעוד סקראם חרתה על דגלה את עקרונות ה"למידה ושיפור תמידי", קהילת הסקראם היא דיי מקובעת ושינויים ו"גמישות" באימוץ הסקראם הם לרוב לא-באים-בחשבון. אירוני משהו.
  • סקראם מכילה חוקים רבים, אך משאירה גם שאלות מהותיות פתוחות: כיצד מפתחים אמורים להתמודד עם בעיות שנובעות מ"צוותי פי'צר" או "עבודה ביחידות קטנות". מתודולוגיות אחרות, בעיקר Extreme Programming ו Lean Startup מכסות רבים מהחורים שלא נפתרו ע"י סקראם – ונפוץ למדי למצוא שילוב שלהן בתוך הסקראם.
    "חסידי הסקראם" נוהגים לדקלם ש "Scrum is a Framework" ועל הארגון להשלים בעצמו את החסר. עדיף היה לו היו מספקים פתרונות (אפילו בדמות XP ו LS).
  • סקראם מתאים לאופי מסויים של אנשים. מקובל מאוד לאמץ סקראם במלואו, הכל או לא-כלום. הגדרות תפקיד כגון "סקראם מאסטר" או "Product Owner" הן מוגדרות היטב ואין כמעט דיון על "וריאציות אפשריות" שלהן. ארגון שגייס אנשים ע"פ פרופיל X – יתקשה לרוב לומר לאנשים יום בהיר אחד "עכשיו עושים סקראם, אתם צריכים להיות Y". כשהוא אומר להם את זה (ראיתי את זה קורה) – יש טלטלה ארגונית גדולה.
אמרנו כבר שאם ניישם סקראם, לא נכון לצפות שהתוצאה תהיה "כמו בספרים".
שאלת השאלות היא אם כן:

האם "סקראם ממוצע" עדיף על "מפל-המים ממוצע"?

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

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

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

בואו נביט בחלון החיפוש של כרום גרסה 22. מוצר בן 4 שנים שנמצא בפיתוח אינטנסיבי:

האם זה לא היישום הפשוט ביותר? זה שמתאים לספרינט ראשון של מימוש הפ'יצר?

לא. אפשר להוריד את האינדקס ("1 מתוך 11"). אפשר גם לוותר על הרקע האדום, שמוצג במידה והחיפוש לא מצא כלום. שניהם לא הכרחיים על מנת לסגור את פונקציונליות החיפוש הבסיסית ביותר.
שני אלו יכולים להכלל בדרישה מאוחרת יותר, שתקרה ספרינט מאוחר יותר, גרסה מאוחרת יותר, או אפילו לעולם-לא.
אם הצוות חושש שגם זה יותר מדי- אפשר לחפור ולצמצם עוד: ויתור על הפינות מעוגלות ב UI, לכתוב קוד נאיבי שלא יעיל  מבחינת ביצועים, לוותר על הכפתורים (למעלה / למטה) ולבצע חיפוש עבור ערך ראשון בלבד. כל אלו אולי חשובים ללקוח, אך ניתן לעשות אותם בספרינט הבא. זכרו: מה שאתם רוצים הוא To Nibble [זהירות! וידאו].

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

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

עלינו לקבל בקשות שמנהל המוצר ביקש ולהשתדל לעשות את המינימום שעליו סוכם. אם זה לא יהיה מספיק – אין מה לדאוג: מנהל המוצר יבין זו במהרה (יש תצוגת-יכולות כל כמה שבועות) ויגדיר PBI נוסף להשלמת הפערים.

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

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

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

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

[ב] תודה לאנונימי על החידוד.

http://en.wikipedia.org/wiki/Lean_Startup

4 כללים למדידת פשטות של תוכנה

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

אחת ההגדרות שאני אוהב היא של בחור בשם J.B. Rainsberger, הגדרה של ״מבנה פשוט של תוכנה״.

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

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

  1. כל הבדיקות עוברות. 
  2. צמצום כפילויות קוד. 
  3. הקוד מתאר כוונה (clarity). 
  4. צמצום  מספר האלמנטים בקוד למינימום האפשרי  אלמנטים שאינם משרתים את מטרות 1-3.

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

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

הנה דוגמה של קוד \"ללא כפילויות\" שהפריע לי והפכתי לכפול:

אני מסכים, זה לא קוד \"מושלם\", וספריית templating הייתה יכולה להפוך אותו ליפה יותר, אך זו דוגמה אמיתית מהחיים.

הנה הקוד לאחר השינוי:

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

קוד המתאר כוונה
אם מזקקים את הכלל של \"כתיבת קוד המתאר כוונה\", לרוב עיקר העבודה היא מסביב לשמות: שמות של פונקציות או משתנים, או שמות חדשים הנוספים ע\"י פעולות \"extract method\" או \"introduce variable\".

ישנן 4 \"דרגות\" של שם:

  1. שם סתמי
  2. שם נכון
  3. שם מדויק
  4. שם בעל משמעות (\"meaningful\").
עצלנות ולחץ גורמים לנו להיצמד לתחתית הסקאלה (1,2), בעוד הקפדה ומקצועיות דוחפים אותנו לראש הסקאלה (3,4).
מאוד נהניתי, כשקראתי את ריינסברגר, כשהוא מתאר בדיוק רב הרגל שגם אני רכשתי: כאשר אני רואה קוד כפול אני מבצע extract method לשורות הכפולות, גם מבלי להבין לעומק מה המשמעות שלהן. פעולה טכנית גרידא.
אני נותן לפונקציה שנוצרה את השם \"foo\". מבלי לחשוב. תוך כדי עבודה אני מבין מה הפונקציה עושה ומשנה את שמה. לאחר 10 דקות עבודה ייתכן והשם שונה כבר שלוש או ארבע פעמים, אך אני מרגיש בבירור שאלו עליות דרגה ברמה של השם. לעתים פשוט צריך לנסות איזה שם ו\"לחיות\" איתו כמה דקות על מנת למצוא שם מוצלח יותר.
צמצום אלמנטים שאינם משרתים את מטרות 1-3. 
למי שנתקל ברעיונות אג\'יליים – אני מניח שעקרון זה הוא מובן מאליו: Eliminate Waste. עדכון המטרה בעיקרון זה היא להימנע ממרכיבים בקוד שלא משרתים את הפונקציונליות, לא מונעים קוד-כפול ולא מסבירים את התנהגות המערכת, כל מיני \"הכנות למזגן\"[ג].

מי שעובד ב (Test Driven Development (TDD נהנה באופן מובנה מעקרון 1 (\"כל הבדיקות עוברות\") ועקרון 4 (\"צמצום אלמנטים לא-חיוניים\"). זו הדרך המהירה ליצירת קוד פשוט, שגם יישאר פשוט לאורך זמן.
מכאן נותרו רק 2 פעולות לשים לב אליהן:
צמצום כפילויות קוד [ב] ו כתיבת קוד המתאר כוונה / קוד ברור. המשיכו לעשות את אלו בצורה תמידית – והמבנה של הקוד שלכם, או ה \"design\" של הקוד שלכם – יהיה פשוט. זו הדרך היעילה ביותר שאני מכיר.
זה אולי נשמע קצת פשוט מדי: אולי ציפיתם לשימוש במחשבון של Cyclomatic complexity וטכניקות של ספירת Weighted Micro Function Points (ממש כמו ספירת קלוריות). 
צר לי לאכזב אתכם: כמה אנשים טובים השקיעו שנים ביצירת מודלים מתמטיים לתיאור סיבוכיות של תוכנה – אך בפועל כמה עקרונות פשוטים הם אלו שיגרמו לקוד שלכם להיות פשוט יותר, וקל יותר לתחזוקה.

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

—-

[א] יש המשתמשים במילה Design על מנת לתאר מצב של תוכנה חיה, ולא רק את התוכניות. לדוגמה: \"Refactoring: Improving the Design of Existing Code\". אם הרעיון הזה ברור לכם – הרגישו חופשיים להשתמש במילה design ולא ב structure.

[ב] אזהרה!: יש המבלבלים בין צמצום כפילות קוד בתוך המערכת, לבין שימוש-חוזר בקוד (code re-usability) או \"צמצום כפילות קוד בעולם האנושי\". בעוד ביטול כפילות-קוד בתוך הקוד שאתם כתבתם הוא כמעט-תמיד דבר טוב, שימוש חוזר בקוד (\"חיצוני\") הוא נושא מורכב עם הרבה ייתרונות וחסרונות. אני מתכנן לעסוק בנושא זה לעומק בפוסט נפרד.

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

המתכנת הרציונלי

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

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

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

לא סביר שכל האנושות, בכל רגע, תפעיל חשיבה עובדתית. …זה לא אנושי, כנראה.

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

הטיות אנושיות

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

לדוגמה: אחת היוריסטיקות החזקות ביותר הידועות היא יוריסטיקת העגינה. ערך מספרי ראשון שאנו שומעים – יעגן אותנו לאותו אזור ערכים ולא נוכל להשתחרר ממנו, לא משנה אם הוא הגיוני או לא. חוקרים הצליחו להטות בצורה משמעותית הערכה מקצועית של שמאים לגבי ערך בתים רק ע\"י כך ש\"זרקו\" מחיר במהלך הביקור – גבוה או נמוך.
יתרה מכך – הסתבר שגם מספר מגוחך לחלוטין שאינו קשור למציאות עדיין משפיע עלינו. \"2.1 טריליון פאונד\" יכול החוקר לזרוק לשמאי שבא לסקור דירת חדר בלוד – ולהשפיע על השמאי בעשרות אחוזים.
לא רק שמאים חשופים להטיה – כל בני האדם חשופים.
ההטייה של יוריסטיקת העגינה פועלת גם כאשר מזהירים את האנשים מראש ומסבירים להם כיצד מתכוננים להשפיע עליהם.
ההטיה פועלת גם בהקשרים-לא-קשורים: סטודנטים נתבקשו להעריך מידות מספריות, מיד לאחר שהם נשאלים מה הספרה האחרונה במספר ת\"ז שלהם. אלו שהספרה שלהם היתה גבוהה (7,8,9) נתנו הערכות גבוהות באופן מובהק מאלו בעלי ספרה נמוכה (1,2,3).

הטיות אנושיות מוכרות מוכרות אחרות כוללות \"מציאת תבנית\" (pattern) לא קיימת בנתונים אקראיים – וייחוס חשיבות לקשר[א], השפעה מתכונה אנושית אחת (למשל: יופי חיצוני) על הערכת תכונה אחרת לא קשורה (למשל: טוב לב, חכמה) – מה שנקרא אפקט ההילה, ועוד.

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

העגל רוצה לינוק
כבני אדם, אנו בנויים על מנת לינוק מידע. יש לנו נטייה ברורה להעדיף דברים שאנו רוצים לשמוע על פני דברים שלא.
כשאנחנו מחפשים אחר מידע, אנו מחפשים בצורה מגמתית שסביר יותר שתוביל אותנו להיכן שאנו רוצים להגיע. מצאתי את עצמי, לא פעם, מבצע בגוגל שאילתות כגון \"SomeTechnology> good performance>\" או \"\'why use TechA\". כמובן, שמצאתי תוצאות שהשביעו את רצוני והמשכתי עמן הלאה: מצגת, שכנוע, החלטה.

כשעשיתי את התרגיל וחיפשתי את השאילתות ההפוכות: \"SomeTechnology> poor performance>\" או \"techA problems\" – מצאתי, לעתים קרובות, תוצאות מספקות שהיו יכולות לשרת אותי אם הייתי רוצה לטעון את ההפיך הגמור.
כנראה שהיה לי נוח להתעלם מכך שהתוצאה שמצאתי היא דעה, לעתים דיי בודדה. מספיק היה לי למצוא 2-3 כאלו בכלל בגוגל – על מנת לקבל את הטענה של הדעה כעובדה.
היו מספר סימני אזהרה שיכולתי לשים לב אליהם – אבל לרוב בחרתי להתעלם. את סימני האזהרה הללו ניתן ללמוד.
עם השנים אני מנסה להשתפר וברגע שאני עושה חיפוש אני מחפש וקורא ברצינות גם את אלו שאומרים את ההיפך. לא תמיד. לפחות כשמדובר בעניינים חשובים ולא בזוטות.

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

כששומעים משהו נפלא, ניתן לרגע להניח שאכן כך, ולחשוב מה ההשפעות הצפויות מכך. האם הנתונים בפועל מצביעים על משהו דומה?
לדוגמה: שמעתי בארוחה עם חברים על רכב מונע אוויר-דחוס הנוסע 200 ק\"מ בעלות של 1$. אין לי שום מושג בנושא – אך לאחר שנים והשקעה רבה כ\"כ ברכבים היברידיים / חשמליים (שלא לומר: פרפטומובילה) – קשה להאמין שהישג רציני ומבוסס כזה הושג מבלי שיציף את כל ערוצי החדשות.

מאמץ להשתמש ב\"שפה גבוהה ומתוחכמת\" או להיצמד לשמות גדולים (גוגל, אפל, וכו\') – יכולים גם הם להיות סימני אזהרה ברורים למידע לא רציני[ב].

הטיית השטן

הסיבה שבגינה יש להיזהר ולבדוק את עצמנו כפליים היא שעל כוכב הלכת ״ארץ״ יש תעשייה שלמה, מכניסה למדי, של ברנשים שכל תפקידם הוא להטות את דעותינו לכיוון זה או אחר. זוהי עבודתם של אנשי שיווק, פרסום ומכירות (לא תמיד הגדרת התפקיד שלהם נשמעת כמו \"שיווק\" או \"מכירות\") – ויש להם אינטרס ולחצים אמיתיים להשפיע על אנשים אחרים ועל החלטותיהם. לעתים קרובות, יש להם משאבים רבים (הוצאות של 50% מכלל התקציב של הארגון על שיווק/מכירות הוא לא מחזה נדיר), ידע מדעי וכלים מעשיים יעילים על מנת לעשות זאת. כלומר: הטעויות בעובדות שאנו עשויים להיתקל בהן, הן לא אקראיות – כי אם מכוונות.
אפשר להקביל זאת להבדל בין בטיחות לאבטחה: בטיחות מגנה עלינו מכשל אקראי, בעוד אבטחה צריכה להתמודד עם אויב תבוני שיחקור ויחפש את הנקודה החלשה ביותר להכות בה. כך גם בקבלת החלטות שבה יש למישהו שמץ של רווח או עניין  – יהיה מישהו שינסה להציג או להבליט נתונים בצורה מגמתית. סביר שאותו אדם ישתמש בהטיות פסיכולוגיות ידועות בכדי להגביר את השפעת דבריו. כנראה שיש לו יותר ידע ואמצעים מאשר \"מטרות ההטיה\". אני מבין שהצהרה זו נשמעת קצת מבהילה, אך זכרו: זה שאתם פרנואידים לא אומר שלא רודפים אחריכם, וקצת יותר ברצינות: במהלך עבודתנו אנו משתמשים בנתונים רבים שיש לאחרים אינטרסים רבים לגביהם. הסיכוי שלכם להיתקל בנתונים שהוטו הוא גדול למדי. לא כל שימוש בנתון מוטה מוביל להחלטה לא-טובה: לעתים קרובות זמינות הנתונים הם אלו שמאפשרים את עצם ההחלטה, אך כדאי להיות מודעים למשחק שמשוחק פה. מרצים בכנסים, ספרים, בלוגים מאמרים במגזינים ודוח״ות אנליסטים, כולם כפופים לניסיונות להטיית הדעה וקידום האג׳נדה. לא מדובר במקרים בודדים – כי אם בשגרה.
הצהרה: בבלוג זה, עד היום, לא הייתה שום הטיה מכוונות או ניסיון לשכנע במשהו שאנני מאמין בו בלב שלם. אם הייתי מקבל ממישהו הצעה אטרקטיבית להבליט משהו (טכנולוגיה, מוצר וכו\'), \"בצורה לא פוגענית ושתשרת בעיקר את האינטרסים של הקוראים\"[ג] – כנראה שהייתי שוקל.

מילת סיכום

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

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

תודה לאביב, ליאורה, ברק, ירון ובעיקר ערן, החבר\'ה של \"ספק סביר\" – פודקאסט ספקני-מדעי (מומלץ!), שנכונו לסייע ולייעץ לגבי פוסט זה.

[א] תרגיל מעניין הוא השמעת קטעים מוזיקליים לאחור – מה שהופך אותם לרצף דיי אקראי, ומציאת \"מסרים סמויים\" ברצף. ב http://jeffmilner.com/backmasking/ תוכלו למצוא כמה דוגמאות. לא סביר שתוכלו להבין משהו מהקטעים שמושמעים לאחור, אך לאחר שתקראו טקסט ההסבר (לדוגמה: \"James Brown is dead\") – יהיה קשה לכם להתכחש לכך שזה בדיוק מה שנאמר שם. לאחר שהורגלו ברעיון (\"זיהינו את התבנית\") – קשה לנו לחזור בנו ולהתעלם ממנה.
[ב] ברוס וויליס ואפל – הם כבר 2 שמות גדולים שמספיקים לסיפור טוב, חסר ביסוס. הנה מכתב שרשרת (בדוי) ששורד כבר 7 שנים ברשת ועדיין נשלח. איך? יש בו כמה שמות גדולים שגורמים לנו כאנשים לנטות ולהאמין…

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