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

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

ייצוג סדרות

02/12/2018

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

bookmarks = [
    'https://www.tocode.co.il',
    'https://www.google.com',
]

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

def even(index):
    return index * 2

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

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

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

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

import sys

with open(sys.argv[1]) as f:
    for index, line in enumerate(f):
        sys.stdout.write(f"{index}: {line}")

print()

והנה גירסא נוספת הפעם שמדפיסה רק את השורות שארוכות מ-10 תווים מתוך הקובץ:

import sys

with open(sys.argv[1]) as f:
    for index, line in enumerate(f):
        if len(line) <= 10: continue
        sys.stdout.write(f"{index}: {line}")

print()

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

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

1, 1, 2, 3, 5, 8, 13, 21

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

def fib(n):
    x, y = 0, 1
    for i in range(n):
        x, y = y, x + y

    return x

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

print(fib(8))

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

i = 0
while fib(i) < 200:
    i += 1

print(f"fib({i}) == {fib(i)}")

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

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

def fib():
    x, y = 0, 1
    while True:
        yield x
        x, y = y, x + y

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

כך נראה קוד שמשתמש בסידרה החדשה שלנו כדי להדפיס את המספר השמיני בסידרת פיבונאצ'י:

for index, val in enumerate(fib()):
    if index == 8:
        print(val)
        break

וזה הקוד שיגלה מהו הערך הראשון שגדול מ-200:

for index, val in enumerate(fib()):
    if val > 200:
        print(val)
        break

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

fib = Enumerator.new do |yielder|
  x, y = 0, 1
  loop do
    yielder << x
    x, y = y, x + y
  end  
end

fib.lazy.each_with_index do |val, index|
  if index == 8
    puts val
    break
  end
end

fib.lazy.each_with_index do |val, index|
  if val > 200
    puts val
    break
  end
end
function *enumerate(it, start=0) {
  for(let x of it) {
    yield [start++, x];
  }
}

function *fib() {
  [x, y] = [0, 1];
  while(true) {
    yield x;
    [x, y] = [y, x + y];
  }
}

for ([index, val] of enumerate(fib())) {
  if (index == 8) {
    console.log(val);
    break;
  }
}

for ([index, val] of enumerate(fib())) {
  if (val > 200) {
    console.log(val);
    break;
  }
}

שימו לב איך ב JavaScript גם המבנה של enumerate לא קיים בשפה ואנחנו בונים אותו באותו תחביר בניית סדרות איתו בנינו את סידרת פיבונאצ'י.

מצב פיתוח לעומת מצב ייצור

01/12/2018

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

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

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

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

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

מה השיטה שלכם?

זה לא בשבילי

30/11/2018

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

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

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

האם זה קצב טוב? האם זה משנה?

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

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

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

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

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

עוד מילה טובה על jQuery

קטע הקוד הבא עובד טוב ב Chrome, Firefox ו Edge וגם על מובייל לא עשה לי עדיין בעיות. רק IE צריך להיות מיוחד:

var form = document.querySelector('#myform');
var fd = new FormData(form);
var xhr = new XMLHttpRequest();
xhr.open('POST', '/submit');
xhr.send(fd);

זאת הדרך הסטנדרטית להגיש טופס ב Ajax. והיא כמעט תמיד עובדת. עד שהיא מפסיקה לעבוד.

וכך נראה טופס שעבורו הקוד הזה נכשל:

<form id="theform" action="/people" method="post">
  Name: <input type="text" name="person[name]" value="John Appleseed">

  <input type="radio" name="person[sex]" value="male"> Male
  <input type="radio" name="person[sex]" value="female"> Female
  <input type="radio" name="person[sex]" value="not_specified"> (Not Specified)

  <input type="submit" name="commit" value="Create Person">
</form>

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

<form id="theform" action="/people" method="post">
  Name: <input type="text" name="person[name]" value="John Appleseed">

  <input type="radio" name="person[sex]" value="male"> Male
  <input type="radio" name="person[sex]" value="female"> Female
  <input type="radio" name="person[sex]" value="not_specified"> (Not Specified)
  <input type="hidden" name="dontcare" value="dontcare" />

  <input type="submit" name="commit" value="Create Person">
</form>

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

שימוש ב jQuery.serialize היה מונע את הבאג וחוסך לי את המחקר, הגילוי העצוב וה Work Around שלא בטוח שמכסה את כל המקרים. אז כן, גם ב 2018, אם אתם יכולים להרשות לעצמכם - שימו jQuery.

אגב תיעוד של הבאג קיים ברשת כבר מ 2014 למשל בקישור הזה: https://blog.yorkxin.org/2014/02/06/ajax-with-formdata-is-broken-on-ie10-ie11.html

פערי ידע

28/11/2018

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

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

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

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

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

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

הדבר היחיד שחשוב

27/11/2018

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

כמה טיפים שאני משתדל לזכור:

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

  2. נוח מאוד לשמור את הלוגים גם במערכת לוגים חיצונית שאפשר לגשת אליה אפילו מהטלפון. יש הרבה כאלה בשוק ובמחירים לא גבוהים (חפשו cloud log storage בגוגל).

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

  4. נוח מאוד לשמור לוג גם של קוד ה JavaScript שרץ אצל הלקוחות שלכם. אפשר לחבר אותם ישירות למערכת הלוגים החיצונית אבל יותר קל להשתמש בהודעת Ajax לשרת שלכם שיתעד את הכל בצורה מסודרת.

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

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

שיקולי עלות/תועלת למתכנתים

26/11/2018

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

(וכשאני אומר יותר מוצלח מה Spec אני מתכוון הרבה יותר מוצלח ממה שהמנהל שלך רוצה).

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

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

אם אי אפשר לנצח אותם...

25/11/2018

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

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

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

תקשורת דו-כיוונית עם socket.io

24/11/2018

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

המשך קריאה

שיתוף פעולה

23/11/2018

שיתוף פעולה הוא אחד הנושאים העדינים שאנחנו מתמודדים איתם.

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

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

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