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

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

לימוד בדחיפה לימוד במשיכה

31/07/2019

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

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

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

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

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

אל תצא כבאי ימושפל

30/07/2019

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

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

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

ההבדל בין:

[print(x) for x in range(10)]

ל:

for i in range(10):
    print(i)

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

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

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

עשר חולשות אבטחה ביישומי ווב OWASP Top 10 עם דוגמאות קוד ב Node.JS

29/07/2019

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

המשך קריאה

איך ללמד כשאין מספיק זמן

28/07/2019

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

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

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

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

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

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

אז Node או Elixir?

27/07/2019

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

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

defmodule HelloWorldWeb.HomeController do
  use HelloWorldWeb, :controller

  def count_lines_in_file(conn, _params) do
    { _, count } = File.stream!("/etc/shells")
                   |> Stream.with_index
                   |> Enum.at(-1)

    json(conn, %{ res: count + 1 })
  end
end

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

בשביל השוואה אותו הקוד ב Node.JS יראה בערך כך:


function countLinesInFile() {
  return new Promise((resolve, reject) => {
    try {
      let count = 0;
      const rl = readline.createInterface({
        input: fs.createReadStream('/etc/shells'),
        console: false
      });

      rl.on('line', (input) => {
        count += 1;
      });
      rl.on('close', (input) => {
        resolve(count);
      });
    } catch (err) {
      reject(err);
    }
  });
}

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

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

קוד ה Node.JS התמודד הרבה יותר טוב עם אותה בדיקה וענה לכל הבקשות תוך 4 שניות, ואף בקשה לא חיכתה יותר מכמה עשרות מילי-שניות עד לקבלת תשובה.

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

https://stressgrid.com/blog/benchmarkinggovsnodevs_elixir/

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

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

חמש סיבות שהקוד שלך לא עובד

26/07/2019

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

  1. יש באג בלוגיקה.

  2. יש באג בתחביר (הסוגריים ששכחת לכתוב אחרי שם הפונקציה - הם הכרחיים).

  3. את לא מריצה את הקוד שאת חושבת שאת מריצה (אולי היתה תקלה בקומפילציה, אולי את מריצה קובץ אחר מזה שפתוח ב IDE, אולי צריך להוריד ולהעלות את השרת).

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

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

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

לא לכל אחד

25/07/2019

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

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

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

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

פתחי מילוט

24/07/2019

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

import os
os.system('mkdir -p foo/bar/mydir')

במיוחד אם לא הכרתם את os.makedirs.

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

async function main2() {
  const res = await db.Label.findAll({
    attributes: [
      [sequelize.fn('COUNT', sequelize.col('Books.id')), 'count'],
      [sequelize.col('name'), 'name'],
    ],
    include: {
      model: db.Book,
      attributes: [],
      required: true,
    },
    group: ['Label.id'],
    });
  console.log(res);
}

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

מחט בערימת שחת

23/07/2019

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

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

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

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

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

טיפ לקיץ - אוביקטים קפואים ב JavaScript

22/07/2019

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

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

> const x = { foo: 10 };
> Object.freeze(x);

> x.foo = 20
> x.bar = 30
> console.log(x);
{ foo: 10 }

> Object.isFrozen(x)
true

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

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

> const x = { foo: [] }
> Object.freeze(x)
> x.foo.push(10)
> console.log(x)

{ foo: [ 10 ] }

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

> const x = 'hello'
> Object.isFrozen(x)
true

> x.foo = 10
> console.log(x.foo)
undefined

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