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

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

ירושה? לא בבית ספרנו

23/09/2022

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

המשך קריאה

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

22/09/2022

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

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

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

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

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

  2. הגבלת גישה למכונות בניה - רצוי לא לאפשר גישה ממחשב פיתוח למכונת בניה, ושהדרך היחידה שמתכנתים יוכלו להכניס קוד חדש היא דרך ה Source Control.

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

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

  5. קוד שנכנס ל Source Control לענף שממנו הולכים לבנות גירסה חייב לעבור Code Review על ידי מספר אנשי צוות אחרים.

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

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

תזכורת לוובינר: פיתוח מערכות ווב עם flask-base

21/09/2022

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

לפייתון יש שני Web Frameworks מרכזיים בשם Django ו Flask. דג'נגו נחשב לפיתרון הוליסטי שייתן לכם תשובה לכל אתגר בפיתוח המערכת, אבל יותר קשה ללמוד אותו ויותר קשה להתאים אותו לכל Use Case בגלל שהוא כולל את הכל. ב Django משתמשים גם בתעשיה במספר חברות גדולות כולל אינסטגרם, דיסקאס, איוונט-ברייט ו Prezi.

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

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

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

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

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

נתראה בוובינר

שבע סיבות בגללן אנחנו לא כותבים בדיקות

20/09/2022

  1. אני לא סומך עליהן. ממילא אחרי כל שינוי אני מריץ את ה flow לבד לראות שהכל עובד.

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

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

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

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

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

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

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

לא למבחן

19/09/2022

״מה עדיף, לעשות שיעורי בית או להצליח במבחן?״

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

אבל כשמסתכלים על החיים ומה חשוב קל לראות שהמטרה הפוכה:

  1. אף אחד לא יודע מראש לאן כדאי להגיע ואיזו רמת מיומנות נצטרך.

  2. לאנשים שונים יש מטרות שונות.

  3. אנחנו מבלים הרבה יותר זמן ב"שיעורי בית" מאשר ב"מבחנים".

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

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

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

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

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

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

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

טיפ טייפסקריפט: זהירות Tuples

17/09/2022

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

const square = [10, 5, 2, 'red'];

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

type User = [number, string, string];

const users: Array<User> = [
    [0, 'a@gmail.com', 'a'],
    [1, 'b@gmail.com', 'b'],
    [2, 'c@gmail.com', 'c'],
];

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

type User = [number, string, string];

const users: Array<User> = [
    [0, 'a@gmail.com', 'a'],
    [1, 'b@gmail.com', 'b'],
    [2, 'c@gmail.com', 'c'],
];

users.push(['d']);

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

type User = [number, string, string];

const users: Array<User> = [
    [0, 'a@gmail.com', 'a'],
    [1, 'b@gmail.com', 'b'],
    [2, 'c@gmail.com', 'c'],
];

const u: User = [3, 'd@gmail.com', 'd'];
u.splice(0, 2);
// u is now: ['d']
users.push(u);

שווה לשים לב שכשמשתמשים ב Tuples כדאי תמיד להוסיף להם Readonly כדי שמשתמשים לא יוכלו (בכוונה או בטעות) לרמות. ככה זה נראה כשה Tuple לקריאה בלבד:

type User = Readonly<[number, string, string]>;

const users: Array<User> = [
    [0, 'a@gmail.com', 'a'],
    [1, 'b@gmail.com', 'b'],
    [2, 'c@gmail.com', 'c'],
];

const u: User = [3, 'd@gmail.com', 'd'];
u.splice(0, 2);

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

Property 'splice' does not exist on type 'readonly [number, string, string]'. Did you mean 'slice'?

הפתעות

15/09/2022

עיקרון בשם POLA, ראשי תיבות של principle of least astonishment, טוען שבעיצוב תוכנה אנחנו רוצים לבנות מערכות שיתנהגו בהתאם לציפיות של רוב המשתמשים. אבל יש פה בעיה - אף אחד לא יכול לדעת מה הציפיות של רוב המשתמשים או מי בכלל יהיו המשתמשים האלה. כשבונים שפת תכנות החיים עוד יותר מסובכים, כי כמעט כל מי שמגיע ללמוד את שפת התכנות שבנית מגיע עם ציפיות משפות תכנות אחרות, וכמעט כל החלטה שתעשה תפתיע הרבה אנשים.

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

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

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

interface Vehicle {
  goto: (place: Place) => void;
  wheels: number;
}

interface Car extends Vehicle {
  testValidUntil: Date;
}

ואותו דבר עם type-ים:

type Vehicle = {
  goto: (place: Place) => void;
  wheels: number;
};

type Car = Vehicle & {
  testValidUntil: Date;
}

אבל אז אני מנסה להגדיר את אותו שדה ב Vehicle וב Car עם טיפוסים שונים (למשל שדה בשם x שב Vehicle הוא מסוג number וב Car הוא מסוג string) ומופתע מההבדל בין השניים: בשימוש ב interface-ים הקוד לא יסכים להתקמפל בגלל שהוא לא יודע מה סוג השדה ב interface היורש (במקרה שלנו Car). בשימוש ב type-ים הקוד יתקמפל אבל סוג השדה ב interface היורש לא יהיה אף אחד משני הטיפוסים המתנגשים אלא יהיה never, מה שאומר שאין בעיה לקמפל את הקוד כל עוד לא יצרתי אף משתנה מה type הבעייתי.

וההפתעה הזאת נפלאה.

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

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

מאחורי הקלעים

14/09/2022

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

הרבה פעמים הפער בין שתי הרמות הוא עצום.

דוגמה קטנה מהעולם של ריאקט - אחד הפיצ'רים החדשים של ריאקט 17 היה שלא צריך לכתוב:

import React from 'react';

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

רמת מאחורי הקלעים כבר מדברת על הסיבות בגללן מישהו התאמץ להכניס את הפיצ'ר הזה, והערך הנוסף שהפיצ'ר הזה נותן לאנשים שכותבים כלים בתוך האקוסיסטם של ריאקט. סבסטיאן מרקבג' וונימין קרול כתבו מסמך הסבר די ארוך באותה תקופה על ההגיון ומאחורי הקלעים של ההחלטה כאן: https://github.com/reactjs/rfcs/blob/createlement-rfc/text/0000-create-element-changes.md

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

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