על בדיקות-יחידה (Unit Testing)

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

מי שניסה Unit Tests מוצלח עלול פשוט להתאהב! כנראה שמעבר לתמורה המעשית – זוהי חוויה רגשית. אחרת, אינני יודע להסביר מדוע אנשי מפתח בעולם התוכנה מתאמצים להוכיח שכתיבת Unit Tests בעצם חוסכת בעלויות הפיתוח[א], מעלה את האיכות, משמחת את הלקוח ומרצה את בעלי המניות… . נראה שהם מאמינים מושבעים המנסים רק להוכיח את אמונתם. מי שחווה את הנירוונה של הרצה של 100+ בדיקות שמסתיימות כולן בהצלחה אחרי ביצוע שינוי מהותי בקוד – מבין את ההרגשה הממכרת. הצבע הירוק זורם ומפיח חיים בעץ הבדיקות שלכם. החרדה מהלא-ברור מתחלפת בשלווה מרגיעה שזורמת בעורקיכם ומפיגה כל זכר לקושי או מתח…

שנייה… להתאהב?! שלווה? נירוונה? – "על מה אתה מדבר?!"
אני יכול להעלות בזיכרוני תמונות של עשרות פרצופים סובלים וממורמרים בעקבות "איזו החלטת הנהלה לכתוב Unit Tests". אז תנו לי להסביר: פעמים רבות ראיתי ניסיונות לאימוץ Unit Tests שגרמו סבל רב למפתחים ו/או לא החזיקו מעמד זמן רב. הייתי אומר שנתקלתי ב 2-3 ניסיונות כושלים לאמץ Unit Tests על כל ניסיון מוצלח.

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

מה הן בעצם בדיקות-יחידה (Unit Tests)?

[מינוח: "קוד פעיל" = productive code. זה שעושה את העבודה.]

בדיקות יחידה הן:

  • מקבילות לקוד הפעיל. לכל חתיכת קוד פעיל יש חתיכה "חברה" של קוד בדיקה שבודק אותו.
  • נכתבת ע"י המפתח שכתב גם את הקוד הפעיל.
  • רצות כל הזמן, תוך כדי פיתוח, כמו "קומפיילר שני".
  • בהערכה גסה, ניתן לצפות לשורת קוד של בדיקות לכל שורת קוד של קוד פעיל.
  • דורשות תחזוקה. ימצאו בהן באגים.
  • דורשות שהקוד הפעיל ייכתב בגישה קצת אחרת: "Testable Code".
  • רצות מהר. ממש מהר[ב].
  • דורשות מאסה קריטית (של כיסוי הקוד הפעיל, לפחות באזור מסוים) – על מנת להיות יעילות.
  • "בונות" אצל המפתחים ביטחון אמיתי בנכונותו של הקוד הפעיל.
  • משפרות איכות פנימית.
  • מובילות אותנו לכתוב קוד מודולרי וברור יותר. בזמן כתיבת הבדיקה, אנו הופכים לרגע מ"יצרני הקוד הפעיל" ל"צרכני הקוד הפעיל" – מה שעוזר מאוד להבחין בקלות השימוש בממשקים / API.
  • משרתות אותנו כתיעוד מעודכן ורב-עצמה.
ההבדל בין בדיקות יחידה שמחזיקות את ההשקעה לבין אלו שלא. שימו לב: לעתים נרצה בדיקות יחידה גם אם הן עולות לנו יותר. מקור:  http://xunitpatterns.com/Goals%20of%20Test%20Automation.html
כתיבה ותחזוקה של בדיקות-יחידה אכן גוזלות זמן, אך מצד שני הן חוסכות ומקצרות תהליכי פיתוח אחרים.
קוד לוגי מורכב יכול להיבדק ישר ב IDE. המתכנת יודע תוך שנייה אם הקוד עובד או לא. האלטרנטיבה, ללא קיומן של בדיקות-יחידה, היא לבצע Deploy של הקוד, להגיע למצב הרצוי במערכת ורק אז לבדוק אם התוצאה היא הרצויה – תהליך ארוך בהרבה.
היכולת לבצע refactoring בביטחון ובמהירות עם סיכוי נמוך לתקלות – גם הוא זרז משמעותי בתהליך הפיתוח. במיוחד בפרוייקטים גדולים בהם אינני יכול להכיר את כל הקוד.ראיתי שני אנשים שישבו ביחד וכתבו אלגוריתם אך הסתבכו: כל רגע מקרה אחר לא עבד. הנטייה הייתה לא לכתוב קוד בדיקות עד שהאלגוריתם לא עובד, "למה לכתוב פעמיים?". דווקא בכך שהתחילו לכתוב בדיקות תהליך הכתיבה הסתיים מהר יותר: אדם אחד שכתב בדיקות הצליח לסיים את המשימה מהר יותר מאלו שלא. הכל בזכות faster feedback cycle.

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

בדיקות יחידה הן לא:
  • נכתבות ע"י אנשי QA, מפתחים צעירים או סטודנטים.
  • כלי יעיל לשיפור איכות חיצונית.
  • נזרקות לאחר שהקוד הפעיל "משוחרר" (shipped).
  • קוד שקל במיוחד לכתיבה.
  • מיוצרות ע"י כלים אוטומטיים (generated). יש להפעיל לא-מעט חשיבה אנושית בריאה על מנת לכתוב בדיקות יחידה טובות.
  • בדיקות אינטגרציה / מערכת / רכיב  component test / פונציונליות functional test / או API test.

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

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

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

האם בדיקות היחידה שלכם מתנהגות יותר כמו בדיקות פונקציונליות? – סימן שיש לכם בדיקות פונקציונליות ולא בדיקות יחידה. עצם זה שאתם משתמשים ב qUnit / nUnit / jUnit לא אומר שהבדיקות שנכתבות הן אכן בדיקות יחידה!

הערה: איני מנסה לטעון ש"בדיקות-יחידה הן טובות" או "בדיקות פונקציונליות הן פחות טובות". פתרון אוטומציה מאוזן כולל לרוב כ 70% בדיקות יחידה, כ 20% בדיקות פונקציונליות וכ 10% בדיקות ל UI. הבדיקות השונות משלימות זו את זו.

מה בודקים בבדיקות יחידה?

אפשר לחלק את הקוד הפעיל ל3 אזורים:

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

  1. קבע את X להיות 4.
  2. השם את X.
  3. קרא את X והשווה שהוא 4.

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

קוד אינטגרציה
קוד זה הוא קוד Gluing שמחבר בין רכיבים / מערכות:

  1. עשה א'
  2. עשה ב'
  3. עשה ג'
במקרים אלו קוד הבדיקה יכלול כנראה mock על מנת להקשיב להתנהגות הקוד והבדיקה תבדוק שא', ב' וג' אכן נקראו. אולי אפילו שהם נקראו לפי הסדר. קוד הבדיקה עלול להיות ארוך משמעותית מהקוד הפעיל. אם הפעולות המדוברות שייכות לאובייקטים אחרים – אדרבא. לעתים קרובות קוד האינטגרציה יכלול אפילו קריאות למערכות אחרות / קוד שלא באחריותנו. עלינו לכתוב Mock Object ועוד Mock Object… בשביל מה כל זה? בשביל לבדוק לוגיקה דיי פשוטה של קריאה לסט פעולות ע"פ הסדר?
למרות שקוד זה רגיש יותר לשבירה במהלך חיי המערכת (תכונה שגורמת לנו לרצות ולבדוק אותו), כל שינוי שלו ידרוש מיד שינוי של הבדיקה. קוד הבדיקה הוא השתקפות (שמנה) של הקוד הפעיל.
מסקנה: ההשקעה בבדיקת קוד שכזה לא משתלמת מבחינת ההשקעה. אולי במערכת שבהן האיכות מאוד חשובה – משתלם להשקיע בבדיקות של קוד אינטגרציה. באופן כללי: התועלת להשקעה – נמוכה.

קוד לוגי (Business Logic)
קוד זה מתאפיין באזורי קוד שהם מופיעות פקודות if ו for (או המקבילות בשפה בהם אתם עובדים) – conditional logic. הקוד לוגי הוא הקוד הרגיש ביותר ל"שבירה". בעצם: לעתים רבות הוא לא כתוב כשורה מלכתחילה!

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

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

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

function FormValidator($form) {

  var username = $form.find('#username'),
      email    = $form.find('#email'),
      error    = $form.find('.error');



  $form.bind('submit', function() {


    if (username.val() === 'Wizard') {
      error.html('Your argument is invalid');
      return false; // prevents the submission of the form
    }
    else if (username.val() !== 'Harry') {
      error.html('Your name is invalid');
      return false;
    }
    else if (!/@/.test(email.val())) {
      error.html('Your email is invalid');
      return false;
    }


  });
};

בפונקציה ניתן לזהות מספר רב של משפטי if, מה שמעיד על כך שיש כאן קוד לוגי – קוד שאנו רוצים לבדוק. גם regular expression (שהתחביר שלו בג'אווהסקריפט הוא טקסט בין סוגרי "/" כמו בביטוי האחרון) הוא קוד לוגי. ביטוי ה regex מבטא תיאור conditional logic כללית שנוכל להזין לה כמה דוגמאות ולוודא שהתוצאה המתקבלת היא הרצויה.

כדי להפעיל את הפונקציה אנו זקוקים להעביר ארגומנט בשם form$ (אובייקט jQuery) שנוצר ממציאת אלמנט form ב HTML שמכיל תגיות (כנראה מסוג input) עם מזהים בשם username וemail. את התוצאה של הרצת הפונקציה נוכל לקרוא מתוך שדה ה error (כמה נוח) בתוך אלמנט ה form.

יש לנו כמה בעיות:

  • הפונקציה החיצונית תרוץ, אך הפונקציות הפנימיות לא יפעלו ללא לחיצה של המשתמש על כפתור "Submit". אולי אפשר "לזייף" לחיצה ב javaScript?
  • ה errors הם text string למשתמש שיכולים להשתנות בקלות. כל שינוי ישבור את הבדיקה => בדיקה רגישה לשינויים.
  • טעינת ה html markup (דף HTML עם javaScript נלווים) עלולה לקחת זמן רב. כלל שהבדיקה אטית יותר – יריצו אותה פחות. בבדיקות JavaScript מקובל להפריד את הבדיקות לקובצי html נפרדים ולהריץ רק אותם – אך עדיין יש פה עבודה לכתוב ולתחזק את ה makrup.
בכתיבת קוד בדיקות בג'אווהסקריפט מקובל לכתוב את בדיקת-היחידה בקובץ html נפרד. ניתן לכתוב קוד שיפעיל פעולת submit ויתפוס את ה error שנזרק. התוצאה: אנו כותבים ומשקיעים יותר שורות קוד וזמן בבדיקה מאשר בקוד הפעיל. לא סימן טוב.
במקרים אחרים, פחות ידידותיים (למשל אסינכרוניות, הסתמכות על סביבה חיצונית כמו טעינת image), כתיבת הבדיקות עלולה להיות פרויקט של ממש. אנו רוצים להימנע מכך.
הקוד למעלה הוא בעצם קוד מעורבב: קוד אינטגרציה וקוד לוגי. אם בבדיקות יחידה עסקנינו, עלינו ללמוד להפריד חלב (אינטגרציה) ואוויר (קוד נטול-לוגיקה) מבשר (קוד לוגי) – ולהתמקד בבדיקות בבשר בלבד. במערכת קיימת ההפרדה היא מאמץ – דבר שמקשה מאוד על ההוספה של בדיקות-יחידה ומפחית את יחס העלות-תועלת. לאחר שביצענו את התרגיל הזה כמה פעמים, יהיה לנו טבעי לכתוב את הקוד מופרד מלכתחילה – כך שכתיבת קוד חדש ובדיק (testable) לא תגזול זמן רב יותר. קוד בדיק הוא גם קוד מודולרי ונקי יותר – כך בעצם אנו מרוויחם פעמיים!

הנה הקוד לאחר שעשינו לו refactoring ובודדנו את הקוד הלוגי – ללא תלות ב DOM:

FormValidator.validate = function(username, email) {

  var errors = [];

  if (username === 'Wizard')
    errors.push('Your argument is invalid');

  else if (username !== 'Harry')
    errors.push('Your name is invalid');

  else if (!/@/.test(email))
    errors.push('Your email is invalid');

  return errors;

}

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

  • החזרת רק קוד ה error והפיכתו לטקסט מאוחר יותר.
  • קבלת כל אלמנטי ה input בטופס כמערך בתור פרמטר לפונקציה – כך שלא יהיה צריך לשנות את החתימה שלה (ושל הבדיקות) בכל פעם שנוסף לטופס פרמטר.

כלי עבודה

  • xUnit – ספרית בדיקה לשפה שלכם כגון nUnit ל #C או jUnit לג'אווה.
  • (CI (continuous integration על מנת להריץ את הבדיקות כל הזמן ולדעת מוקדם ככל האפשר – כשמשהו נשבר.
  • Code Coverage Tool – על מנת לדעת כמה מהקוד שלכם עובר בדיקה. למשל Emma ל Java או NCover ל #C.

אם אתם עובדים עם Java ו Jenkins (כלי CI) יש תוספת מאוד נחמדה בשם Sonar שעושה גם Code Coverage בעזרת Emma ומאפשרת ביצוע Drill Down שימושי למדי.

על מדד ה Code Coverage
אחד הדברים שמנהלים אוהבים לעשות הוא להציב לצוות יעדים מספריים. (Code Coverage (CC הוא מדד מספרי ברור וגלוי (אם הוא משתקף באיזה דו"ח שקל להגיע אליו) המתאר את כמות הקוד הפעיל שמכוסה ע"י בדיקות-היחידה. החישוב הוא % שורות הקוד הפעיל שהופעלו בעת הרצת כל בדיקות היחידה הקיימות אצלנו.

בעבר פיתחתי רגשות שליליים עזים למדד ה Code Coverage. הייתי חבר בפרוייקט בו נדרשנו ל 100% כיסוי – לא פחות. המערכת הייתה מערכת P2P Video Streaming שהייתה כתובה חציה ב Java/AspectJ וחצייה ב ++C. בקוד היו חלקים גדולים שהתנהגו בצורה סינכרונית, והיו פנייות רבות לפעולות I/O (לרשת). על מנת לבדוק חלקים מסויימים בקוד נאלצנו לכתוב את הבדיקות עצמן בצורה אסינכרונית: השתמשנו בספריית ה Concurrency של Doug Lea בעיקר עבור קוד הבדיקות – ורק מעט עבור הקוד הפעיל. זה היה בשנת 2003 ומקורות הידע בבדיקות-היחידה היה מצומצם.

הקזנו נהרות של דם בכדי להעלות למעל 90% CC. הכי גבוה שהגענו היה 93%-לרגע, כך נדמה לי.
תכונה בולטת של CC הוא המאמץ השולי הגובר: להשיג 50% זה יחסית קל. כל אחוז מעל 80% הוא עבודה מתישה וקשה – ובכל רגע נתון מישהו יכול לבצע submit של קוד לא-בדוק ו"לזרוק" את הצוות אחוז או שניים אחורה.
על האחוזים מעל 90% אין לי מה לדבר. זה נראה כמו טעות חישוב – כי לא נראה היה שאפשר באמת להגיע לשם. הוספנו לקוד הפעיל getters[ג] רק כדי "לסחוט" עוד קוד קל-לבדיקה ולשפר אחוזים. הערכנו שאנו משקיעים פי 3 זמן על קוד בדיקות מאשר על קוד פעיל (מצב לא בריא בעליל, אני יודע כיום לומר). מאז ראיתי פרוייקט שבו השקיעו כשליש מהזמן על כתיבת בדיקות, והחזיקו ב CC של כ 80% באופן שוטף.

כמה מסקנות:

  • התמודדות לא מוצלחת בכתיבת בדיקות-יחידה עלולה לגרום למפח נפש רציני.
  • היעד הסביר, לדעתי כיום, ל CC תלוי מאוד בסוג המערכת:
    בקוד שרובו אינטגרציה / UI / IO אני חושב שבריא לשאוף ל 60-70% CC.
    בקוד שרובו parsing או לוגיקה טהורה (לדוגמה XML Parser) אפשר בהחלט לשאוף ל 80-90%.
    במערכת שהיא באמצע (כמו רוב המערכות) אני חושב שיעד בריא הוא 70-80% CC. אני מציין טווח ולא מספר מדויק כי CC הוא מדד "חי". בכל Submit של קוד הוא ישתנה. להישאר כל הזמן בטווח של 10% – הוא משהו שאפשר לדרוש מצוות. במיוחד במערכת גדולה.
  • CC הוא תנאי מחייב אך לא מספק. אם יש לכם 20% CC – בטוח שהקוד שלכם לא בדוק טוב. אם יש 80% CC – אזי זה סימן טוב אבל יש לעבור על הבדיקות ולוודא שהן משמעותיות. ניתן בקלות להשיג CC גבוה עם בדיקות חלקיות ולא יעילות.
  • למרות שבתאוריה ב TDD אמור להיות 100% CC, תאוריה זו תיאוריה: כמעט תמיד יהיו חלקים של חוק אינטגרציה, Adapters למשל, שלא נכון או כדאי לבדוק אותם.

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

(TDD (Test Driven Development
TDD הוא סוג של "השלב הבא" בבדיקות-יחידה. הרעיון הוצג ע"י קנט בק – גורו וחדשן אמיתי בעולם התוכנה.

ראיתי מספר פרשנויות מוזרות שמתהדרות בשם "TDD". למשל: צוות שכל תחילת ספרינט השקיע 2-3 ימים בכתיבת מסמכי בדיקה מפורטים בוורד, כתב את הקוד ואז בסוף הספרינט מימש את הבדיקות בקוד, שבכלל היו בדיקות פונקציונליות / מערכת.
אם אתם רוצים להתהדר ב TDD או אפילו ATTD (נשמע טוב!) – לכו על זה. שום חוק מדינה לא יאסור על מאלף-סוסים, נאמר, להתהדר בכך שהוא "עובד TDD", אז בטח לא על מישהו שעוסק בתוכנה…

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

  • איך אתם יודעים שתצליחו לכתוב קוד בדיק (testable) ולא תאלצו לעשות בו שינויים אח"כ? – פשוט כתבו כל בדיקה בסמיכות רבה לקוד שהיא בודקת. אולי אפילו כיתבו את הבדיקה לפני.
  • איך תוודאו שהכיסוי שלכם של בדיקות הוא אופטימלי? – החליטו שקוד פעיל נכתב רק על מנת לגרום לבדיקה לרוץ בהצלחה. "מה שלא בדוק – לא קיים".
  • איך תפחיתו באגים בבדיקות שלכם – ופרט את המקרים של בדיקות שעוברות בהצלחה גם כאשר הקוד לא עובד כשורה? כתבו והריצו את הבדיקות קודם לכתיבת הקוד וודאו שהן נכשלות.
כלומר: קודם כותבים את קוד הבדיקה, ורק לאחר מכן – את הקוד הפעיל.
TDD הוא הפעלה חוזרת של סדר הפעולות הבא:
  1. הבינו מה הפונקציונליות (קטנה) שאתם רוצים להוסיף למערכת.
  2. כתבו בדיקת-יחידה שבודקת את הפונקציונליות הזו, קצה-אל-קצה.
  3. הריצו את הבדיקה: עליה בהכרח להיכשל, מכיוון שהפונקציונליות לא כתובה עדיין![ד]
  4. כתבו פיסת קוד הפשוטה ביותר שאפשר שתעביר בדיקה בודדת[ה]. אם הריצה הצליחה המשיכו לחתיכה הבאה וכו'.
  5. לאחר שכל הבדיקות ירוקות (עברו בהצלחה) – המשיכו לפונקציונליות הבאה.
הסבבים ב TDD מאוד קצרים: כתיבה – הרצת בדיקות, כתיבה – הרצת בדיקות.
המתכנת מחליף תדיר 2 כובעים: כותב הקוד הפעיל וכותב הבדיקות, והוא אמור לנסות בכל כובע "להערים" על הטיפוס השני ולספק לו את המינימום האפשרי. כלומר: להערים על עצמו.
אני לא בטוח שאפשר להסביר בקלות את סגנון העבודה מבלי לבצע תרגילים בפועל. יש בו אלמנט של משחק – שבהחלט עשוי לגרום להנאה.
בפועל TDD מסייע:
  • להתמקד במטרה ולכתוב רק קוד שנדרש ("eliminate waste")
  • להשיג בדרך הקצרה והפשוטה קוד בדוק עם Code Coverage גבוה.
  • לכתוב את ה API / interface של כל מחלקה בראייה של הלקוח (= הבדיקה) – דבר שמסייע ליצירת API טובים וברורים.
  • מכריח אתכם לכתוב קוד מודולרי ובדיק (testable).
הסיבה העיקרית, לדעתי, ש TDD הוא לא כל-כך נפוץ היא שחלק גדול מאלו ש"עובדים עם בדיקות-יחידה" בעצם עובדים על עצמם בעיניים – הם כותבים בדיקות פונקציונליות או בדיקות בכיסוי מזערי. ברגע שיש לכם תהליך של כתיבת בדיקות-יחידה שעובד, המעבר ל TDD הוא השלב הטבעי הבא.

סיכום 

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

  • הבדיקות נכשלות תדיר – גם כשהקוד הפעיל עובד בסדר => בדיקות רגישות לשינויים (fragile tests).
  • הבדיקות שלכם בודקות קוד שלא סביר שיישבר (למשל קוד מה JDK)
  • הבדיקות שלכם נכשלות בהמוניהן – כמו בדיקות פונקציונליות.
  • אתם עושים שימוש תדיר ורב ב Mock Objects (שהם בעצם סוג של patch לקוד קשה-לבדיקה).
  • יש לכם בדיקות שמשאירות "עקבות" לאחר שרצו. זו יכולה להיות בעיה או בבדיקות או בקוד הפעיל.
  • הבדיקות שלכם רצות לאט.
כמה מדדים לשימוש בריא בבדיקות-יחידה:
  • הבדיקות רצות מהר.
  • אתם מריצים את הבדיקות ("עץ ירוק") גם כשלא נדרש. זה פשוט מרגיע אתכם.
  • כאשר אתם נכנסים לקוד חדש, הדרך הטבעית ביותר היא לעבור ולקרוא את קוד הבדיקות.
  • בדיקות שנכשלות אכן תופסות באגים (רגרסיה או בכלל) לפני שאתם עושים submit.
  • כולם מתמכרים לרעיון של הבדיקות.

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

—-

[א] ע"פ התאוריה האקדמית, השקעה באיכות לרוב מעלה את עלויות הייצור – לא מוזילה אותן.

[ב] כאשר בדיקות יחידה מתחילות לרוץ לאט (נאמר, כפול מהקומפיילר או יותר) מחלקים אותן ל 2 חבילות: "בדיקות מהירות" שרצות כל הזמן, ו"בדיקות אטיות" שמריצים מדי פעם כמו ב CI או לפני פעולת submit של קוד.

[ג] שלפנים – בעברית. כמובן.

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

[ה] בלימוד TDD מלמדים שאם אתם בודקים את הפונקציה (add(a,b והבדיקה הראשונה בודקת ש 2+2=4 – יהיה נכון לכתוב את גוף הפונקציה בצורה הכי מוכוונת-מטרה שאפשר, כלומר return 4. זאת על מנת לוודא שאינכם כותבים קוד מיותר ("מה שלא בדוק לא קיים") אך גם להרגיל אתכם לכתוב בדיקות מספיק מקיפות שבודקות את הפונקציה במספר מספיק של מקרים.

ארכיטקטורה: Quality Attributes (חלק ב' – כיצד משתמשים)

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

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

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

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

Security (נקרא במקור Integrity) – עד כמה מוגבלת / מאובטחת צריכה להיות הגישה לנתונים / שירותים של המערכת.

Efficiency – עד כמה יעילה המערכת בניצול משאבי החומרה העומדים לרשותה, או בניצול שירותים אחרים (כגון SOA) – והחומרה העומדת לרשותם.

Portability – היכולת של המערכת לרוץ בסביבות ריצה שונות, קרי מערכות הפעלה, סביבות ענן, תצורות רשת וכו'.

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

Interoperability – היכולת להחליף נתונים בקלות עם מערכת אחרת. באופן אישי לא מצאתי מאפיין זה שימושי. Interoperability נראה לי יותר כמו פיצ'ר נקודתי מאשר יכולת רוחבית. אני משאיר אזכור לצורך שלמות הרשימה המקורית.

Maintainability – עלות תפעול (Administration / Operations) נמוכה של המערכת. מה שנקרא TCO (Total Cost of Ownership). מאפיין זה יכול להצביע על הוספת "פ'יצרים של ניהול המערכת". עוד נושא שנכנס לקטגוריה זו היא קלות ההתקנה של המערכת, לעתים מתייחסים אליה כמאפיין איכות עצמאי: Installability.
דוגמה: כלי monitoring שיסייעו לנטר בעיות במערכת. השקעה ב Upgrade קל וכו'.

Developability (נקרא במקור Flexibility) – היכולת לפתח את המערכת בקלות, לבצע בה שינויים או לתקן באגים. מה שנקרא TCD ((Total Cost of Development מאפיין איכות זה עוזר לאפיין את הדילמה שבין איכות שמוסיפה סיבוכיות למערכת (Portability או Efficiency, למשל) למול היכולת לפתח מהר וביתר קלות.

Extensibility – היכולת להרחיב ולהוסיף יכולות חדשות למערכת, בעזרת Plug-in, API וכו'. כמובן שיכולת ההרחבה היא ליכולות / אזורים ספציפיים במערכת.

Supportability – היכולת לתמוך במוצר בקלות, אם מדובר בגוף המייצר את התוכנה או גוף שלישי. כלי Monitoring, לוגים קריאים, יכולת לבצע Dumps של זיכרון או נתונים, דו"ח על תצורת המערכת וכו'.

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

Testability – היכולת לבצע בדיקות למערכת. באופן אישי, מאפיין זה נראה לי כמו אמצעי להשגת Reliability או Developablity – ולא מטרה בפני עצמה.

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

Availability – מאפיין זה תקף בעיקר לשרתים (או שירותים) והוא מורכב מזמינות (אחוז הזמן שהשרת / שירות זמין ללקוחותיו) או תדירות הכישלונות (מה שנקרא MTBF). שרת יכול להיות זמין 99% מהזמן אך לכשול לשנייה אחת – כל יום, מה שיגרום לתקלות כואבות. (MTBF (Mean Time Between Failures עוזר להשלים את התמונה שנתון הזמינות לבדו לא מספק היטב.

רשימת "מאפייני איכות" ממקורות שונים.  מקור: http://www.clarrus.com/documents/Software%20Quality%20Attributes.pdf

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

Extensibility, Maintainability ו Supportability הם סוג של "פיצ'רים" שאולי מנהל המוצר לא ייזום ולכן על הארכיטקט להציע ולקדם. לכאורה נראה שהם שונים משאר מאפייני האיכות מכיוון שהם "פיצ'רים נוספים למערכת". בפועל – השקעה בכל מאפייני איכות (למשל Availability או Safety) דורשת עבודה וההבדל בין שלושת מאפייני איכות אלו לשאר היא קטנה משנדמה במבט ראשון.

בחירת מאפייני איכות

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

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

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

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

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

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

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

במשך שנים, נושא חם בשוק ה IT היה בעיית ה Lock-In: בחרת ספק מסוים, לו יש API מסוימים. השתמשת בהם והיית מרוצה, אך ביום בו הספק מעלה מחירים / מחפף בשירות / קמים מתחרים עדיפים – "אין לכם שום דרך לבצע שינוי".
האמירה של "אין שום דרך" היא אמירה לא מדויקת – אך מכיוון שהסיפור (אלמנטים טרגיים של בגידה?) הוא סיפור מוצלח – הוא הושרש והשאיר חותמו על אנשים רבים.

אחד ה "Best Practices" שנבעו מכך הוא לכתוב קוד "בלתי תלוי בספק (vendor)" בכלל, ו"בלתי תלוי בספק בסיס-הנתונים" בפרט. עבדתם עם מערכת אורקל ומחר אתם רוצים לעבור ל MS-SQL? אין בעיה – מחליפים רק את ה "connection string" – והכל עובד.

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

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

בפועל, ההחלטה המעשית הייתה לבחור במאפיין האיכות "no DB vendor lock-in", על פני "Developability" ועל פני "Scalability".

רק להבהיר: אני מדבר על מקרה בו אתם מוכרים Appliance או Hosting. כאשר הלקוח צריך לטפל בבסיס נתונים בעצמו, ברור למדי שלחברות רבות יש העדפה ברורה איזה בסיס נתונים הם רוצים להתקין: אולי יש להן DBA מוכשר שמכיר אותו, אולי הן כבר ביצעו השקעה מסוימת בטכנולוגיה (כלי ניהול, אבטחה וכו') ספציפית לספק בסיס-נתונים זה.
מתן האופציה להתקין כל DB, עבור לקוחות רבים – יכולה להחשב כאיכות של Maintainability – כי אז הם יוכלו להתקין את בסיס הנתונים שהם מעדיפים.
הבחירה של פלטפורמות ג'אווה וNET. לבצע הפשטה שכזו (בעזרת ODBC ו JDBC) באה לתמוך במי שרוצה להשיג Maintainability.
אם כל מה שאני שומר בבסיס הנתונים היא טבלה או 2 של נתונים פשוטים – אין לי רווח רב משימוש בבסיס נתונים ספציפי.


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


דרך מומלצת 
על מנת להשתמש במאפייני איכות בצורה יעילה כדאי לבחור כמה תסריטים עיקריים במוצר ולהדגיש אותם:
"בתחום ה X אנו מעדיפים d על c ומעדיפים b על a".אפילו כדאי לצרף דוגמאות ספציפיות, מה שנקרא "תסריט":

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

Reusability

עוד תחום בו סקירת מאפייני האיכות יכול לסייע רבות הוא בהחלטה האם לעשות שימוש חוזר בקוד או "איחוד" של 2 מודולים בעלי פונקציונליות דומה.
לעתים רבות יש לנו 2 ספריות / מודולים שרשימות היכולות שלהן, כ checklist היא דומה או אולי זהה. לדוגמה: ספרייה לשליחת הודעות דוא"ל. שאלה מתבקשת היא "מדוע אנו מתחזקים 2 ספריות שעושות אותו הדבר? האם זה לא בזבוז?"
לפני שאתם רצים לאחד את הקוד לספרייה יחידה, כדאי לבחון מהם מאפייני האיכות של כל ספרייה.
אם ספרייה אחת מקפידה על reliability (מכיוון שהיא משמשת לשליחת התרעות מערכת חמורות), בעוד השנייה מתמקדת ה customizability (היכולת להגדיר הודעות דואר יפות ומותאמות-אישית) – ייתכן ואין סתירה וניתן לשלב אותן בהצלחה. כלומר, ספרייה אחת לא תוכל להחליף מייד את השנייה, אך הגיוני לקחת חלקים מכל ספרייה ולאחד אותן. חשוב להבין אלו הנחות נעשו על מנת לאפשר את מאפייני האיכות העיקריים של כ לספרייה ולראות שהם לא מתנגשים.
לעומת זאת, אם אחת מתמקדת ב customizability והשנייה ב performance/scalability (שליחת אלפי הודעות בדקה) – קרוב לוודאי שטוב תעשו אם תשמרו אותן כ2 ספריות נפרדות.
לעתים דיי קרובות, ספריות שאיחודן נראה כ no brainer במבט ראשון, מתגלות במהרה כלא סבירות לאיחוד לאחר שבוחנים את מאפייני האיכות שלהן.

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

נ.ב. – חלק מהספרות הפורמלית בנושא מתאר את "מאפייני האיכות" קצת אחרת – אני התייחסתי אליהן בצורה שהייתה שימושית עבורי והוכיחה את עצמה. חפשו בגוגל "Quality Attributes" ותוכלו למצוא חומר רב.



[א] הגפרור שאנו מכירים.
[ב] אני מכחיש בתוקף!, אך בפועל ייתכן שפעם אני הוא זה שיזם את המהלך.
[ג] Effective, לא efficient.

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

לאחרונה יוצא לי קצת להתעסק בהרמת הבלוג.

אחת החוויות החיוביות והמפתיעות שהיו לי היא התקנה של הwidget של Outbrain. ה widget הוא תיבת ה \"בנוסף אולי תאהב/י את…\" שנמצא פה בבלוג בתחתית כל פוסט (הוא זקוק לעוד זמן תוכן ותעבורה כדי לעבוד כמו שצריך פה בבלוג).

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

הממ… עסוק באמצע העבודה כדי לפרט? טיפים? אומר לעצמו \"הוא טכנולוג, הוא כבר יסתדר\"?

לא ציפיתי לכיף חיים. הוספתי גוגל אנליטיקס ממש לפני זה ונאלצתי לחפש tutorial בגוגל, לערוך את ה HTML View של הבלוג ולשתול javascript שנתנו לי. ואז עוד עשר דקות בפורומים של גוגל להבין ש \"invalid connection\" בסוף ההתקנה הוא נורמאלי וייקח לו עד חצי שעה להתרפרש.
גוגל אנליטיקס ובלוגר הם שני מוצרים של גוגל – חברת ענק עם גוף פיתוח שידוע באיכותו הגבוהה. ואאוטבריין? סטארט-אפ ישראלי?

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

פתאום הייתי בבלוגר – מאשר להוסיף? מאשר.
נגמר.

מה???
בדקתי כמה פעם לא מאמין – הכל עובד.

אני לא יודע אם החוויה שלי היא המייצגת, אבל היא בהחלט הייתה טובה. הדהדו לי קולות של מנהלי מוצר שעבדתי איתם בעברי: \"המשתמש הוא administrator – הוא יסתדר גם עם להקליד regex במקום להקליק על checkbox\". \"זה משתמש טכנולוגי, אז שימחק הכל וייצר את הרשימה מחדש, הוא ימצא עזרה בגוגל\".

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

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

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

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

מה עושים?
מנהל בכיר שהכרתי הציע ברגע של יאוש לשלוח מאמר בנושא ל all-customers@worldwide.com, המייל חזר.

הייתי שמח מאוד לראות חרם צרכנים של אנשי IT: \"לא Wizard – לא קונה\". מהפכת צרכנים כמו שקרתה בעולם המערבי בשנות החמישים.*

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

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

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

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

ויש את המציאות הקיימת שנדרש להמשיך להתמודד איתה.

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

בקיצור, גמר חתימה טובה וצום קל.

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


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