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

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

מתי להשתמש בספריה חדשה

18/10/2020

רוב המתכנתים (אני כלול), כשהם שומעים על ספריה חדשה מיד הם חושבים "וואו איזה מדליק! אני חייב לנסות את זה".

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

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

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

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

נ.ב. גירסה יותר עדינה שעושים גם מתכנתים יחסית אחראיים נשמעת ככה: אני צריך להוסיף מנגנון Drag & Drop למערכת, אני לא יודע כלום על DnD אז אחפש ברשת ספריה קיימת של DnD לריאקט, אני רואה שיש הרבה התלהבות מ React DnD אז אשלב אותה אצלי בפרויקט. זו בדיוק אותה הטעות כמו לשלב את Vue במערכת רק בגלל שרציתם ללמוד Vue. רק בגלל שאתה צריך מנגנון Drag and Drop לא אומר שאתה צריך דווקא את ספריית React DnD.

מכבה את היד

17/10/2020

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

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

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

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

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

איך לקבל רשימה של שמות כל הפונקציות מאוביקט JavaScript

16/10/2020

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

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

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

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

והפונקציה Object.getPrototypeOf מחזירה את הפרוטוטייפ של אוביקט מסוים.

בואו נראה את זה בפעולה עם קצת קוד. קודם כל מחלקה בודדת:

class A {
  hello() {
    console.log('hello world');
  }
}

const a = new A();

const prototypeOfA = Object.getPrototypeOf(a);

// This works
a.hello();

delete(prototypeOfA.hello);

// But this doesn't
a.hello();

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

לעומת זאת אם אני מנסה למחוק את הפונקציה hello מהאוביקט a עצמו אין שום בעיה להמשיך לקרוא לה (כי ממילא היא לא היתה שמורה על a):

class A {
  hello() {
    console.log('hello world');
  }
}

const a = new A();

const prototypeOfA = Object.getPrototypeOf(a);

// This works
a.hello();

delete(a.hello);

// And this works too
a.hello();

אפשר לעשות משחקים יותר מתוחכמים עם Object.create:

const a = { one: '1' };
const b = Object.create(a);
const c = Object.create(b);

// All 3 work:
console.log(a.one);
console.log(b.one);
console.log(c.one);

b.two = '2';

// undefined
console.log(a.two);

// these two work:
console.log(b.two);
console.log(c.two);

c.three = '3';
// undefined
console.log(a.three);
console.log(b.three);

// but this works
console.log(c.three);

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

עכשיו לשאלה - איך לקבל רשימה של שמות כל הפונקציות מאוביקט JavaScript?

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

למזלנו ב JavaScript יש פונקציה בשם Object.getOwnPropertyNames שמחזירה את רשימת כל המאפיינים של אוביקט מסוים. שילוב שלה עם Object.getPrototypeOf יפתור לנו את הבעיה עם לולאה פשוטה:

function getAllMethodNames(obj) {
  let res = [];
  let p = obj;

  while (p !== Object.getPrototypeOf({})) {
    const ownKeys = Object.getOwnPropertyNames(p);
    const ownMethods = ownKeys.filter(n => n !== 'constructor').filter(n => typeof p[n] === 'function');
    res = [...res, ...ownMethods];
    p = Object.getPrototypeOf(p);
  }

  return res;
}

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

class A {
  hello() {
    console.log('Hello world');
  }
}

class B extends A {
  byebye() {
    console.log('bye');
  }
}


const a = new A();
const b = new B();

console.log(getAllMethodNames(a));
console.log(getAllMethodNames(b));

הקוד ידפיס תחילה את hello, בגלל שזו הפונקציה היחידה במחלקה A, ואז בשורה חדשה את hello ו byebye שהן שתי הפונקציות שאפשר להפעיל על האוביקט b מהמחלקה B.

מאיפה להתחיל

15/10/2020

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

יהיו מתכנתים שיבחרו להתחיל מה UI ויבנו קודם את ה HTML/CSS (או יקנו תבנית מתאימה)

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

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

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

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

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

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

היום למדתי: מפרידי מספרים בכל מיני שפות

14/10/2020

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

x = 10_000_000;

# prints: 10000001
console.log(x + 1);

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

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

# perl
use v5.32;

my $x = 10_000_000;
say($x + 1);

רובי לקחה כל מה שיכלה מפרל וזה כמובן עובד גם שם:

# ruby

x = 10_000_000
puts x + 1

ופייתון שהוסיפה את הסימן באזור גירסה 3.6:

x = 10_000_000
print(x+1)

ג'אווה 7 לקחה את אותו סימן והקוד הבא עובד שם:

public class Main {
  public static void main(String [] args) {
    int x = 10_000_000;
    System.out.println(x + 1);
  }
}

ורק C++ בחרה בסימן המוזר של גרש בודד:

#include <iostream>
using namespace std;

int main() {
  int x = 10'000'000;
  cout << x + 1 << endl;
}

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

מכירים איך להפריד ספרות בשפות אחרות? ספרו לי בתגובות.

מתכנתים טובים לא נותנים למשתמשים שלהם לירות לעצמם ברגל

13/10/2020

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

  1. אם אני יכול בתור משתמש לשרוף לעצמי את המסך, אז מי שכתב את הדרייבר עשה עבודה לא טובה.

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

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

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

מדריך קוד: בואו נכתוב אפליקציית הודעות בזמן אמת עם Pheonix ו React

12/10/2020

פיניקס היא פריימוורק צד השרת האהובה עליי בימים אלה. היא לקחה הרבה השראה מ Rails, כתובה בשפת Elixir ורצה בתוך המכונה הוירטואלית של Erlang. היא מספקת ביצועים מעולים ליישומים המבוססים על Web Sockets (קצת כמו Node.JS), ובאותו הזמן מגיעה עם כל הבטריות בפנים (קצת כמו Rails).

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

המשך קריאה

עזבו - שגם לא יראה נקי

11/10/2020

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

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

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

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

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

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

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

מחשבים לא משקרים

10/10/2020

שלוש מילים ששינו את חיי.

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

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

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

אפס טעויות

09/10/2020

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

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

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

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

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