הבלוג של ינון פרק

טיפים קצרים וחדשות למתכנתים

תיקונים

16/04/2018

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

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

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

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

ונעבור לחלק המקצועי

15/04/2018

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

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

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

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

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

משוררים ושרברבים

14/04/2018

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

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

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

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

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

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

tr -cs A-Za-z '\n' |
tr A-Z a-z |
sort |
uniq -c |
sort -rn |
sed ${1}q

כשהמציאות והתיעוד לא מסכימים

13/04/2018

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

use strict;

my %h = (
  foo => 10,
  bar => 20,
  buz => 30,
  has => 40,
  bil => 50,
  boo => 60,
);

print join(', ', values(%h)), "\n";

לא משנה כמה פעמים תריצו את הקוד הזה הוא תמיד ידפיס את המספרים 10,20,30,40,50,60 באותו הסדר. ועדיין התיעוד של פרל ציין במפורש שסדר המספרים שיודפסו במקרה כזה הוא מקרי אז אל תסתמכו עליו. אבל אנחנו הסתמכנו.

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

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

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

נ.ב. בנושא Push Notifications על אנדרואיד כדאי לקרוא כאן: https://medium.freecodecamp.org/why-your-push-notifications-never-see-the-light-of-day-3fa297520793

תרבות עבודה

12/04/2018

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

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

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

מסקנות?

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

תבניות מחשבה ושפות תכנות

11/04/2018

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

public class a {
  public static String checkSecurity(String [] people) {
    String found = "";
    int i=0;
    while (i < people.length && found.equals("")) {
      if (people[i].equals("Don")) {
        // report Don
        found = "Don";
      }
      if (people[i].equals("John")) {
        // report John
        found = "John";
      }
      i += 1;
    }
    return found;
  }

  public static void main(String [] args) {
    String res = checkSecurity(args);
    System.out.println(String.join(", ", args));
    if (res != "") {
      System.out.println("Found intruder: " + res);
    }
  }
}

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

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

interface StringPredicate {
  boolean check(String candidate);
}

public class b {
  public static String find_first(String [] people, StringPredicate p) {
    for (int i=0; i < people.length; i++) {
      if (p.check(people[i])) {
        return people[i];
      }
    }
    return null;
  }

  public static String checkSecurity(String [] people) {
    return find_first(people, (name) -> (name.equals("John") || name.equals("Don")));
  }

  public static void main(String [] args) {
    String res = checkSecurity(args);
    System.out.println(String.join(", ", args));
    if (res != "") {
      System.out.println("Found intruder: " + res);
    }
  }
}

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

def find_first(items)
  items.each do |item|
    break item if yield(item)
  end
end

found = find_first(ARGV) { |name| %w[John Don].include?(name) }
puts "Found Intruder: #{found}"

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

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

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

10/04/2018

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

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

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

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

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

  5. ורק בסוף אחרי כל הנזקים שאנחנו עושים לעצמנו יושבים ה״האקרים״, מסתכלים בהנאה על חורי האבטחה שאנחנו בונים עבורם.

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

דברים ש POC לא בודק

09/04/2018

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

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

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

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

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

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

טעויות מול שטויות

08/04/2018

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

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

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

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

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

יוניקוד בקטנה

07/04/2018

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

המשך קריאה