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

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

היום למדתי: התקנת חבילות אופליין עם 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 בלבד לא חושפת מידע שאסור לחשוף. מתכנת כזה לעולם לא ייפול בבורות אבטחת מידע טפשיים ורבים כל כך שאנחנו רואים במערכות ווב היום.

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

שטויות שחשוב לשים לב אליהן במבחן מעשי

18/10/2019

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

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

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

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

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

  4. כדאי להיזהר מ Over Engineering. לכתוב עשרות קלאסים וירושות בשביל משימה קטנה כנראה ישאיר רושם שאתם אנשים שאוהבים לסבך דברים.

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

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

בהצלחה!