על ניהול ובדידות

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

כמו בשירו של רנדי ניומן, Loneley at the top, ככל שאתה עולה גבוה יותר, כך אתה בודד יותר.

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

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

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

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

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

מה כן אפשר לעשות:

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

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

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

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

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


מקווה שתרמתי במשהו,

עידן

איך "לנצח" ראיונות עבודה

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

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

כל מרואיין הוא עולם ומלואו. 

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

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

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

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

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

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

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

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

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

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

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

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

עידן

על עבודה מהבית ועתיד שוק העבודה

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

למה לא עבדנו מהבית עד היום?


דבר זה קרה בגלל מספר סיבות:

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

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

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

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

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

יתרונות של עבודה מהבית


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

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

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

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

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

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

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

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

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

חסרונות של עבודה מהבית


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

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

ושימו לב -

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

מה צופן העתיד? חלק א' - הבנת שוק העבודה


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

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

דור ה-Y (או דור המיליניום) הוא הדור שנולד במהלך שנות ה-80 ושנות ה-90 של המאה הקודמת. 

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

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

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

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

דור ה-Z הוא הדור שלאחר דור ה-Y, ילידי שנות ה-2000 ואילך.

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

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

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

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

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

מה צופן העתיד? חלק ב' - ההשערות שלי


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

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

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

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

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

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

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

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

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

לסיכום


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

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

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

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

מה שבטוח, תמונה של אוכל מושכת את העין.

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

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

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

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

ואז בת הזוג שלכם שואלת - כמה זמן יקח לכם לקלף את תפוחי האדמה?

בכדי לענות על השאלה, ספרתי כמה תפוחי אדמה חתכתי ב-5 דקות האחרונות.
התשובה הייתה 10 תפוחי אדמה.

ספרתי כמה נשארו לי על השיש - 30 תפוחי אדמה.
חישוב קליל אומר - קצב של 10 ל- 5 דקות, דהיינו 2 תפוחי אדמה לדקה.

נשארו 30, קצב של 2 בדקה..
יקח לי 15 דקות! כמה פשוט וכמה קל.

אבל רגע.

הנחת היסוד (1) היא שאנחנו משווים תפוחי אדמה עם תפוחי אדמה.

האם כל תפוחי האדמה זהים?

אם היו זהים בגודלם, החישוב היה מדויק.

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

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

הנחת היסוד (2) - אני יודע בדיוק כמה עבודה אני דרוש לבצע ומתי היא מסתיימת.

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

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

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

הנחת יסוד (3) - ביצועי העבר מעידים על העתיד.

אמרנו שלקח לי 5 דקות לחתוך 10 תפוחי אדמה.
ונניח ונדלג על התסבוכת הקודמת ונשארו אך ורק 30 על השיש, ללא אורחים נוספים.
אמרנו שיקח 15 דקות, נכון?

וכמה תיקח אותה משימה אם... חבר שלי יחתוך במקומי?

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

האם ביצועי העבר שלי אומרים משהו על ביצועיים עתידיים?

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

סוף האנלוגיה - והנה כמה מסקנות.

כדי להעריך נכון ולהיות תותח על באסטימציות:

1.  צריך להשוות תפוחים לתפוחים.
2.  צריך לדעת בדיוק דיי גבוה מה על השולחן.
3.  צריך לחזות את העתיד בצורה מדויקת מבחינת הפתעות.
4.  צריך שביצועי העבר ישקפו את ביצועי העתיד בצורה מדויקת.

בעולם הפיתוח:

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

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

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

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

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

נורא ואיום, הא?

כמה טיעונים נוספים שלא הגיעו לעולם תפוחי האדמה:

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

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

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

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


מה עושים בכל אופן? 

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

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

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


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


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


Fried Potatoes 

- מידע נוסף - חפשו באינטרנט noestimates#
- הרעיון לאנלוגיה של תפוחי האדמה, קרדיט ל-John Yorke ולסרטון הזה.
- פוסט חשוב שמדבר על הרעיון ועל ה-Debate בנושא שממנו תרגמתי חלק עבור הפוסט הזה, כאן.

למה פרויקטי אוטומציה נכשלים?

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

בואו נתחיל מההתחלה. 
אני לא מכיר אף חברת תוכנה שמרוצה מכיסוי האוטומציה וממצב האוטומציה שלה. 

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

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

למה כרטיס הגרלה? כי בדיוק כמו כרטיס הגרלה, ההצלחה בפרויקטים האלה היא לרוב קטנה. קטנה מאד.

Image result for trash bin

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

1. אין יעד ברור לפרוייקט האוטומציה


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

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

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

2. האוטומציה באה להחליף את האנשים שבודקים כיום


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

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

3. טסטים שכתובים רע


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

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

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

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

4. יחס בין השקעה לבין רווח (ROI)


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

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

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

5. חוסר ידיעה של מה יש ומה אין


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

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

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

6. טסטים שלא רצים או נותנים פידבק בזמן לא סביר


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

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

7. פיזור האחריות על האוטומציה ברחבי הארגון


דמיינו רחוב ריק בהם בו רק אתם ואדם נוסף.
פתאום האדם הנוסף מקבל התקף לב.
סביר להניח שתעצרו ותעזרו לו.

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

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

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

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

---

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

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

לסיכום:

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

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


זהו.
בהצלחה לכולם.

על IEnumerable ו- IQueryable

הפעם אחדד נקודות על הממשקים IEnumerable ו- IQueryable ועל ההבדלים ביניהם.

נתחיל מהפשוט והמוכר יותר, IEnumerable. 
ממשק זה הוא בעצם מימוש בשפת C# של ה-Design Pattern הקרוי Iterator, המאפשר לנוע בתוך Collection איבר אחרי איבר.

Collection שמממש את הממשק הזה (לדוגמה List), מאפשר שימוש במשפטי foreach, כאשר אחזור האובייקטים יתבצע בצורה עצלה (Lazy) בצורה איטרטיבית.

אותו foreach יבקש בכל איטרציה איבר אחד בלבד, שיחזור מאותו Collection ע"י משפט Yield (או מימוש IEnumerator על הנתונים ואחזור שלהם).

למה צריך דבר שכזה?

1. לפעמים הפעולה של להחזיר את כל האיברים היא כבדה ונרצה לתת למי שמשתמש בקוד שלנו את האופציה לפעול בצורה Lazy.

    //If you will try to do ToList to this collection, it may take along time...    
  
    public class Top100PageCrawlerCollection : IEnumerable<object>
    {              
        public IEnumerator<objectGetEnumerator()
        {
            for(int i=0;i<100;i++)
                yield return DownloadPage();
        }
      
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
      
        public object DownloadPage()
        {
            System.Threading.Thread.Sleep(100);  
          
            return new Object();
        }
    }

2. לפעמים הפעולה של להחזיר את כל האיברים היא בלתי אפשרית (דוגמה למטה).

    // If you will try to do toList to this collection, you will be stuck...
  
    public class PositiveNumbersCollection : IEnumerable<int>
    {  
        private int _innerseed = 0;
              
      
        public IEnumerator<intGetEnumerator()
        {
            while(true)
                yield return _innerseed++;
        }
      
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
      
    }

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


עכשיו נעבור ל-IQueryable: הבסיס מאחורי IQueryable הוא תמיכה בעצי ביטויים (Expression Trees). פעולות על Collection כגון פלטור לא יתבצעו על מאגר הנתונים, אלא ישמרו בטיפוס נתונים של ביטוי ורק ברגע ההרצה תתבצע פעולה הנקראת קמפול - הפיכה של אותו ביטוי לשאילתה הסופית שתרוץ אל מול הנתונים במקום המרוחק.

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

מבולבלים? דוגמה:

            DBCollection Db = new DBCollection();
          
            var query1 = Db.AsEnumerable().Where(x => x.Age < 50).Run();
          
            // SELECT * FROM Collection
          
            var query2 = Db.AsQueryable().Where(x => x.Age < 50).Run();
          
            // SELECT * FROM Collection WHERE age < 50

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

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

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

עידן

מותו של ה-Code Review

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

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

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

אז איך זה יכול להיות שאנחנו מוקפים בקוד לא טוב למרות שאנחנו מתמידים בתהליך ה-Code Review?

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

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

מה לגבי Code Review בפורמט שהוא מתבצע היום?
לדעתי הוא לא מספיק יעיל וטוב.

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

2. הוא מבזבז כמות עצומה של זמן לאנשים - כל מי שהיה ראש צוות \ מתכנת בכיר והופצץ בכמויות של Code Review, הדבר "שותה" את מרבית הזמן הפנוי במקום להתעסק בכתיבת קוד, עבודת מחקר או מחשבה איך לקדם את הצוות. 

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

פתרון:

אני מציע Pair Programming חלקי. 

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

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

אם יש טייס וטייס משנה, למה אין מתכנת ומתכנת משנה? (שמתחלפים מדי פעם)

אז למה ציינתי Pair Programming חלקי? מה זה בעצם החלקי הזה?

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

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

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

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

תחשבו על זה,
עידן

למה אני פחות אוהב פונקציות Initialize ומה אפשר לעשות בנידון

יצא לי כמה פעמים לנהל דיון כזה או אחר על מחלקות שמכילות פונקציית Initialize.

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

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

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

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

var primeGenerator = new RandomGenerator();             
var num = primeGenerator.GetNext();

לעומת:

var primeGenerator = new RandomGenerator();      
primeGenerator.Initialize(seed:DateTime.Now); 
var num = primeGenerator.GetNext();


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

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

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

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

אספקט נוסף הוא קריאה לפונקציות וירטואליות, שיכול להיות הרסני מאד ב-Constructor - דוגמה:

public class Base
{
        public Base()
        {
            Foo();
        }
  
        protected virtual void Foo() {}
    }

    public class Derived : Base
    {
        private object _object;
  
        public Derived()
        {
            _object = new object();
        }
              
        protected override void Foo()
        {
            _object.ToString();    //Null Reference!!!!
        }
    }

בגלל שסדר הקריאה ל- Constructors ביצירה של Derived הוא קודם כל ל- Constructor של Base, תהיה קריאה לפונקציה הוירטואלית שתגיע ישר למימוש שנמצא בתוך Derived ומשם נקבל Exception כיוון שעדיין לא בוצע אתחול ל-Object.

עוד חסרון בכתיבת קוד מורכב ב-Constructor - אולי לא שמתם לב או חשבתם על זה אף פעם, אבל אין ל-Constructor ערך מוחזר.
במידה והאתחול יכשל, אי אפשר יהיה להחזיר Null או false. המקסימום שנוכל לעשות הוא לזרוק Exception ולהשאיר את האובייקט שבקושי נולד במצב לא יציב.
חוץ מזה, מי רוצה לזרוק Exception ב-Constructor?
לצפות דבר שכזה, משמעו לעטוף קוד נוסף בבלוקים של Try וזה לא דבר יפה, צפוי או נכון במיוחד לעשות.

פתרונות -

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

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

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

כעת תהליך הבניה יהיה צפוי יותר, מורכב יותר ויוכל להחזיר שגיאות.


     public sealed class SQLConnector : IDBConnector
    {
        internal SQLConnector(string connectionString)
        {
            Connect(connectionString);
        }
   
        private void Connect(string connectionString)
        {
            // Initialize Logic...
        }
   
        public void Query(string query)
        {
            //Query Logic, Must be connected
        }           
    }

    public class PersistencyFactory()
    {
        public IDBConnector CreateDBConnector(string connectionString)
        {
            //here we can return something, handle excpetions, insert more creation logic and return a valid object            
            return new SQLConnector(connectionString);
        }
    }

פתרון נוסף - אתחול סמוי רק כאשר צריך, מה שקרוי Lazy Loading.

    public class SQLConnector : IDBConnector
    {
        private string _connectionString;
        private bool _isConnected = false;
   
        public SQLConnector(string connectionString)
        {   
           _connectionString = connectionString;
        }
   
        private void Connect(string connectionString)
        {
            if(_isConnected) return;
       
            // Initialize Logic...
       
            _isConnected = true;
        }
   
        public void Query(string query)
        {
            Connect(_connectionString); //Lazy...
       
            //Query Logic, Must be connected or will get excpetion
        }           
    }

תהנו,
עידן



הבוליאני הרע

פרמטר בוליאני, דבר תמים, האמנם?

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

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

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

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



































פשוט, לא?

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

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

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

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












תהנו,
עידן

איך לכתוב בדיקות יחידה בצורה נכונה

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


גם מאמרים באינטרנט וגם אנשים לרוב מדברים על - Arrange-Act-Assert , שיטת שלושת השלבים למבנה של בדיקת יחידה.

אבל מה מעבר לזה? מה חשוב בקוד שכזה (חוץ משהבדיקה תעבוד)?

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

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

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

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

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

כן כן, שמעתם טוב - מותר לשכפל קוד.

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

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

בחזרה לעניין, נסו להבין מה הקוד זה עושה:






















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

ועכשיו תראו איזה קסם.
ככה הקובץ שלי נראה אחרי Refactor קל:




















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

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

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

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






























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

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

על הדרך תרוויחו את זה שתתאהבו במבחנים שאתם כותבים, תאמינו לי :)

נספח קצר על שם המבחן - אני אישית מעדיף את המבנה הזה:

ModuleName_ActionDescription_ExpectedResult

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

זהו,
עידן