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

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

בגדול זה עובד

11/04/2024

ג'ואי צ'נג (אני מקווה שאני כותב את השם הזה נכון) עשתה עבודה מטורפת כדי לאפשר ל node.js לטעון עם require מודולים של ESM. היא כתבה על זה בבלוג שלה כאן:

https://joyeecheung.github.io/blog/2024/03/18/require-esm-in-node-js/

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

בקיצור ג'ואי צ'נג כתבה PR שמאפשר לקוד CJS לעשות require לקוד ESM, שזה כבר מאוד משפר את המצב להרבה מצבים. אבל זה עדיין עקום כי זה לא מטפל בבעיה האמיתית, שהיא הקומפילציה ל CJS רק בשביל שדברים יעבדו כמו שצריך עם מודולים ישנים ב npm.

(כי אם הכל היה ESM לא היינו צריכים לטעון ESM עם require).

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

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

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

ואם אין before בספריית הבדיקה שלך?

10/04/2024

בימים אלה אני בונה מחדש את קורס node.js שבאתר. הגירסה החדשה תכיל המון TypeScript ותכסה בנוסף ל node גם את Deno ו Bun והמטרה שלי היא שרוב הקורס יעבוד בכל שלושת סביבות הריצה.

בגדול המצב של TypeScript בצד שרת הוא מאוד טוב וגם דינו וגם באן מספיק בשלים בשביל לכתוב עליהם, אבל מדי פעם יש שטויות ובעיות תאימות. דוגמה קטנה היא שכשטוענים את המודול test של node מתוך deno אין תמיכה ב before.

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

import { Database } from '@/db_types.ts'
import { Kysely } from 'kysely'
import { DenoSqliteDialect } from "@soapbox/kysely-deno-sqlite";
import { DB as Sqlite } from 'https://deno.land/x/sqlite/mod.ts';

export const useDB = async (test: (db: Kysely<Database>) => Promise<void>) => {
  const _db = new Kysely<Database>({
    dialect: new DenoSqliteDialect({
      database: new Sqlite(':memory:'),
    }),
  });

  await _db.schema
    .createTable('contact_info')
    .addColumn('id', 'integer', (col) => col.primaryKey())
    .addColumn('name', 'text', (col) => col.notNull())
    .addColumn('email', 'text', col => col.unique())
    .execute()

  try {
    await test(_db);
  } finally {
    await _db.destroy();
  }
}

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

test('POST /contacts created a new contact', async () => {
  await useDB(async db => {
    await superdeno(app(db))
    .post('/api/v1/contacts')
    .set('Accept', 'application/json')
    .send({name: "a", email: "a@gmail.com"})
    .expect(200);

  const res = await superdeno(app(db))
    .get('/api/v1/contacts')
    .set('Accept', 'application/json')

  assert.deepEqual([
        { id: 1, name: "a", email: "a@gmail.com" }
      ], res.body);
  })
});

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

רק מרגיש ככה

09/04/2024

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

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

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

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

"הפעם זה בטוח יצליח. בטוח."

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

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

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

מה המילה שמופיעה הכי הרבה פעמים במובי דיק?

08/04/2024

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

https://www.gutenberg.org/cache/epub/2701/pg2701.txt

עכשיו בואו נלך לקרוא אותו, אבל בהילוך מהיר.

המשך קריאה

מה שעשינו פעם קודמת

07/04/2024

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

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

אבל אני לא בטוח שזה כל כך פשוט. הנה עוד כמה נקודות למחשבה:

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

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

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

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

כן, באמת משתמשים בזה

06/04/2024

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

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

type DeepPartial<T> = T | 
(T extends Array<infer U> 
    ? DeepPartial<U>[] 
    : T extends Map<infer K, infer V> 
        ? Map<DeepPartial<K>, DeepPartial<V>>
        : T extends Set<infer M>
            ? Set<DeepPartial<M>>
            : T extends object ? {
                [K in keyof T]?: DeepPartial<T[K]>;
            } : T);

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

בואו נקרא את ההגדרה יחד-

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

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

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

  4. גם לא מפה? לא להיבהל אולי זה Set. אם כן אז בעצם אנחנו צריכים סט מטיפוס DeepPartial של הפריטים ב T.

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

  6. לא אוביקט? טוב אז נשארים עם T כי זה בטח משתנה פשוט.

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

הבחירה היא לא בין הצלחה לכישלון

05/04/2024

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

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

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

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

שלוש תחנות בדרך למציאת עבודה בהייטק

04/04/2024

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

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

המשך קריאה

דינו או באן

03/04/2024

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

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

בכל ההשוואות שמצאתי בין שלושת הכלים באן היה המהיר ביותר, עם יעד להיות כמה שיותר תואם ל node. בגירסה 1.1 שיצאה עכשיו הם מספרים בהתלהבות על ה APIs הלא מתועדים של node שהם משכפלים.

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

לאן זה הולך?

קשה לי לדמיין עתיד בו node.js ממשיך להוביל. אם באן יעבדו נכון מהר מאוד אנשים יתחילו להחליף את ה node שלהם ב bun בלי להרגיש בהבדל. ובדיוק כמו שאף אחד לא מתגעגע לוובפאק גם לא נראה געגועים ל node. הדבר החדש יעבוד בדיוק אותו דבר רק מהר יותר.

ועדיין אני מקווה לראות את דינו לוקח את ההובלה. יש להם קהילה מפרגנת ומיתוג טוב וכוונה אמיתית ליצור משהו טוב יותר. אם זה יקרה זה לא יהיה Drop In Replacement אלא בחירה חדשה שנצטרך לעשות. אני אשמח להתעורר יום אחד ולגלות שכמו שלא הייתי צריך יותר את jQuery אני גם לא אצטרך את כל ה require, ה Buffer וה APIs הלא מתועדים.

אבל אני לא משתמש ב xz

02/04/2024

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

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

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

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

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