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

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

מישלב

28/10/2019

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

ההבדל בין שתי הפונקציות הבאות הוא תהומי, למרות ששתיהן כתובות באותה שפה ועושות בדיוק את אותו דבר:

function ReturnFirstElement(array) {  
  if (array.length > 0) {
    var result = array[0];
  }
  else {
    var result = undefined;
  }
  return result;
}

const returnFirstElement = (arr) => arr[0];

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

function fib1(n) {
  let [x, y] = [0, 1];
  for (let i=0; i < n; i++) {
    [x, y] = [y, x + y];
  }

  return x;
}

function fib2(n) {
  let [x, y] = [0, 1]
  for (let i=0; i < n; i++) {
    [x, y] = [y, x + y]
  }

  return x
}

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

למה ואיך לפתוח בלוג מקצועי

27/10/2019

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

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

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

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

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

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

https://www.tocode.co.il/workshops/88

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

היום למדתי: התקנת חבילות אופליין עם Node.JS

26/10/2019

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

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

הנה השוואת זמנים קטנה בשביל לעשות גם לכם חשק:

$ time npm install --save express
real    0m3.625s

$ time npm install --offline --save express
real    0m1.646s

עכשיו רק נשאר למצוא איך להגביל את גודל ספריית ~/.npm כדי שלא תתפוס לי את כל הדיסק.

עבד אצלי על המכונה

25/10/2019

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

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

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

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

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

טיפ וובפאק: הדרך הכי קלה להבין איך וובפאק בונה את הבאנדל שלכם

24/10/2019

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

הדגל display-modules מציג את כל המודולים שוובפאק הכניס לבאנדל, והדגל display-reasons מציג את הסיבות. בהפעלה לדוגמא זה נראה כך:

localhost:after ynonperek$ npx webpack --display-reasons
Hash: 1a41b54711f73da75381
Version: webpack 4.39.2
Time: 387ms
Built at: 10/23/2019 9:02:26 PM
 Asset      Size  Chunks             Chunk Names
app.js  1.16 KiB       0  [emitted]  main
Entrypoint main = app.js
[0] ./src/main.js + 1 modules 387 bytes {0} [built]
    single entry ./src/main.js  main
    | ./src/main.js 58 bytes [built]
    |     single entry ./src/main.js  main
    | ./src/panel.js 329 bytes [built]
    |     harmony side effect evaluation ./panel  ./src/main.js 1:0-28
    |     harmony import specifier ./panel  ./src/main.js 3:0-5

הבאנדל כולל נקודת כניסה אחת בשם ./src/main.js שמורכבת משני קבצים: הקובץ main.js הוא נקודת הכניסה, והקובץ panel.js שיובא באמצעות import מתוך main.js. יותר מזה - אנחנו רואים ששורה 1 בקובץ main.js היא שהפעילה את ה import (בגלל ההודעה harmony side effect evaluation שנמצאת לידה), ובשורה 3 בקובץ main.js יש קריאה לפונקציה מתוך הקובץ panel המיובא.

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

מאה אחוז כיסוי קוד

23/10/2019

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

def square(x):
    return 4

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

import unittest

class TestSquare(unittest.TestCase):

    def test_square(self):
        self.assertEqual(square(2), 4)

if __name__ == '__main__':
    unittest.main()

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

מול הקוד הבא בריילס:

class Person < ApplicationRecord
  validates :name, presence: true
end

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

המחברת הקטנה

22/10/2019

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

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

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

אז אם אתם מחפשים תירוץ טוב לחזור מהחופש ולקנות מחברת הנה שלוש סיבות שלי:

  1. כשאנחנו כותבים דברים אנחנו משתפרים בהם. מי שכותב כל יום דף "היום למדתי" ילמד יותר דברים במהלך היום.

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

  3. מחברת עוזרת לחשוב יותר לעומק על שאלות קשות. רוב האנשים אוהבים שאלות קלות, כלומר שאלות שאפשר למצוא את התשובה עליהן בחיפוש ברשת. שאלות קשות הן שאלות הרבה יותר מעניינות כמו "באיזה מצבים נעדיף להשתמש ב ES6 Classes ובאיזה מצבים ב React Hooks". אפשר ליצור טבלא כזו במחברת וכל פעם שיש לכם רעיון לכאן או לכאן לרשום אותו במקום המתאים. לעולם לא תוכלו לתת תשובה מלאה, אבל ככל שתלמדו יותר כך הטבלא תהיה מלאה יותר והמחברת תעזור לכם להתמקד.

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

טיפ npm: השלמה אוטומטית של פקודות מ package.json

21/10/2019

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

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

npm completion >> ~/.bashrc

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

$ npm run <Tab>

נקבל השלמה אוטומטית מתוך הסקריפטים שמוגדרים בבלוק scripts ב package.json.

קריא למי שמבין

20/10/2019

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

print''.join(list(open('../fd/0'))[1:])[-26:]

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

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

(rows == good_line).all(axis=1).any()

ואולי עדיף לכתוב לולאה במקום. ההבדל הגדול בין שתי השורות - במקרה הראשון קריאות לא היתה המטרה, ולכן גם אם אתם מכירים טוב אם print, list ו open עדיין יהיה קשה להבין מה קורה שם. במקרה השני הקוד קריא למי שמבין. אם אתם מכירים טוב את numpy ובפרט את הפונקציות all ו any שלו, לא תהיה לכם שום בעיה להבין מה השורה עושה. סיכוי טוב שזה יהיה אפילו יותר ברור מאשר הלולאה המקבילה.

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

הרגלי עבודה

19/10/2019

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

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

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

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

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