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

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

טיפ ריאקט: טיפול באירועים גלובאליים

12/10/2022

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

export default function App() {
  const [keys, setKeys] = useState("");

  function handleKeyDown(e) {
    setKeys(keys + e.key);
  }

  return (
    <div className="App" onKeyDown={handleKeyDown}>
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <p>{keys}</p>
      <input type="text" />
    </div>
  );
}

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

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

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

import { useEffect, useState } from "react";
import "./styles.css";

function GlobalKeyboardEvents({ onKeyDown }) {
  useEffect(() => {
    document.body.addEventListener("keydown", onKeyDown);
    return function () {
      document.body.removeEventListener("keydown", onKeyDown);
    };
  }, [onKeyDown]);
  return <></>;
}

export default function App() {
  const [keys, setKeys] = useState("");

  function handleKeyDown(e) {
    setKeys(keys + e.key);
  }

  return (
    <div className="App" >
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <p>{keys}</p>
      <input type="text" />
      <GlobalKeyboardEvents onKeyDown={handleKeyDown} />
    </div>
  );
}

ואפשר לשחק איתו לייב בארגז החול בקישור: https://codesandbox.io/s/frosty-violet-llfo2g?file=/src/App.js:0-711

מחשבות שמאטות אותי

11/10/2022

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

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

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

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

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

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

היום למדתי: איך לדבג תוכנית בקונטיינר אחר

10/10/2022

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

המשך קריאה

זהירות: סקריפטים בפרודקשן

09/10/2022

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

I am experiencing this breaking change, my production app is broken and unusable to my clients.

Me three. Prod is down. Got woken up here by confused users

It affected my app in production too, axios is not a function.

Confirmed as breaking production applications on my end as well. This is a big miss in testing coverage for a library like this.

Also having multiple broken production apps here.

Experiencing issues as well on production apps. Error: "axios is not a function"

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

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

<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

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

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

גם בפרויקט קטן

08/10/2022

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

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

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

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

אז למה בעצם להתאמץ?

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

להתקדם בלי לזוז

07/10/2022

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

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

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

Nokia, our platform is burning.

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

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

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

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

  2. ללמוד, גם (ובמיוחד) דברים שלא קשורים לעבודה.

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

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

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

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

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

הסתכלות קדימה בביטויים רגולאריים

06/10/2022

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

This crate provides a library for parsing, compiling, and executing regular expressions. Its syntax is similar to Perl-style regular expressions, but lacks a few features like look around and backreferences.

מה הם אותם look around features ואיזה כוחות על הם מוסיפים לביטויים רגולאריים? בואו נגלה.

המשך קריאה

התחיבות או הערכה

05/10/2022

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

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

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

טיפ טייפסקריפט: מחליפים if ב switch

04/10/2022

קוד הטייפסקריפט הבא לא מתקמפל, למרות שאין בו שום דבר לא בסדר:

type AB = "a"|"b";

function test(x: AB): number {
    if (x === "a") {
        return 1;
    } else if (x === "b") {
        return 2;
    }
}

זה הלינק לפלייגראונד אם אתם צריכים הוכחה: https://www.typescriptlang.org/play?#code/C4TwDgpgBAggQlAvFARAQxQHxQIxQbgChCAzAVwDsBjYASwHsKpgIBnYACgA8AuWOAJR8KZALY4IAJygBvQlAVRaJKNySJk6FANnzF+yRGBlJTAIxF9AXygQANq2jLVXdZrw65+g0ZNMATJaKVoRWQA

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

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

function test(x: AB): number {
    switch(x) {
        case "a": return 1;
        case "b": return 2;
    }
}

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

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

function test(x: AB): number {
    if (x === "a") {
        return 1;
    } else if (x === "b") {
        return 2;
    } else {
        // never happens
        return 0;
    }
}

שמתקן את הבעיה אבל לא מגן עליי משינוי עתידי ב AB.

וגם את זה שסובל מאותה בעיה:

// @ts-ignore
function test(x: AB): number {
    if (x === "a") {
        return 1;
    } else if (x === "b") {
        return 2;
    }
}

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

function test(x: AB): number {
    return {
        "a": 1,
        "b": 2,
    }[x];
}

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

פרילאנס וערך ללקוח

03/10/2022

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

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

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

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

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