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

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

לא יודע לתכנת

14/09/2024

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

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

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

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

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

ספריות בסיסי נתונים וטיפול בהבדלים בין בסיסי הנתונים

13/09/2024

ב Rails יצרתי טבלה בבסיס הנתונים עם קוד שנראה בערך כך:

create_table :users do |t|
  t.string :username
  t.string :email
  t.timestamps
end

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

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

'use strict';

module.exports = {
  up: async (queryInterface, Sequelize) => {
    await queryInterface.createTable('Users', {
      id: {
        type: Sequelize.INTEGER,
        autoIncrement: true,
        primaryKey: true,
        allowNull: false
      },
      username: {
        type: Sequelize.STRING,
        allowNull: false
      },
      email: {
        type: Sequelize.STRING,
        allowNull: false
      },
      createdAt: {
        type: Sequelize.DATE,
        allowNull: false,
        defaultValue: Sequelize.NOW
      },
      updatedAt: {
        type: Sequelize.DATE,
        allowNull: false,
        defaultValue: Sequelize.NOW
      }
    });
  },

  down: async (queryInterface, Sequelize) => {
    await queryInterface.dropTable('Users');
  }
};

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

export const users = mysqlTable('users', {
  id: bigint('id', { mode: 'number' }).primaryKey().autoincrement(),
  fullName: varchar('full_name', { length: 256 }),
}, (users) => ({
  nameIdx: index('name_idx').on(users.fullName),
}));

וזו מיגרציה בקיסלי שכתובה בצורה מפורשת עבור Postgresql:

  await db.schema
    .createTable('person')
    .addColumn('id', 'serial', (col) => col.primaryKey())
    .addColumn('first_name', 'varchar', (col) => col.notNull())
    .addColumn('last_name', 'varchar')
    .addColumn('gender', 'varchar(50)', (col) => col.notNull())
    .addColumn('created_at', 'timestamp', (col) =>
      col.defaultTo(sql`now()`).notNull(),
    )
    .execute()

למה הם עושים את זה? ומה עדיף?

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

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

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

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

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

טיפ Hono שימו לב לנתיבים שלא מסתיימים בלוכסן

12/09/2024

את הקוד הבא לקחתי מתוך התיעוד של hono כדי להגיש קבצים סטטיים:

app.use('/client/*', serveStatic({ root: './'}));

אם ניגשים לנתיב /client/foo.js יוגש הקובץ foo.js מתוך תיקיית client. אם ניגשים ל /client/index.html יוגש הקובץ index.html ואם ניגשים לנתיב /client/ יוגש גם כן הקובץ index.html כצפוי.

אבל מה קורה אם ניגשים ל /client (בלי לוכסן בסוף)? הונו מנסה ללכת לקראתנו ואוטומטית מגיש שוב את תוכן הקובץ index.html. הבעיה היא שה URL נשאר בלי הלוכסן, ולכן אם index.html כולל הפניה לקובץ אחר ההפניה תהיה יחסית ל / ולא יחסית ל /client. לדוגמה נניח שבתוך index.html מופיעה השורה:

<link rel="stylesheet" href="style.css" />

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

פיתרון מהיר הוא להוסיף הפניה מ /client ל /client/ עם הקוד הזה:

app.get('/client', (c) => {
  return c.redirect('/client/', 301); // Permanent redirect
});
app.use('/client/*', serveStatic({ root: './'}));

ואם אתם מכירים פיתרונות אחרים אל תתביישו לשתף בתגובות.

איך לקרוא Code Review של Chat GPT

11/09/2024

הבעיה עם Chat GPT ו Code Reviews היא שאין ל GPT עדיין מספיק קונטקסט בשביל להבין למה בניתי דברים כמו שבניתי ולכן הוא ימליץ על Best Practices כלליים, גם כשזה לא הכרחי, וגם יכול לטעות ולחשוב שלא השתמשתי ב Best Practice מסוים אפילו שכן. הנה שתי דוגמאות קטנות שעזרו לי לראות מה GPT רואה ומה הוא מפספס.

המשך קריאה

אבל מי יתחזק את זה?

10/09/2024

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

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

  2. הפרויקט משתמש בטכניקת עבודה שאף אחד לא חשב עליה קודם (זה גאוני! ידעתי שהיינו צריכים לבנות ORM בעצמנו).

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

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

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

  6. קוד משתנה אבל התיעוד שמלווה אותו נשאר ללא שינוי.

  7. אף אחד לא יודע איך או מתי צריך לשדרג תלויות.

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

בחירת כלים לפרויקט צד

09/09/2024

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

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

כן צריך לשים לב לשתי טעויות שאנשים עושים סביב הצלחות כאלה-

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

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

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

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

אז OpenAI עברו לרמיקס

08/09/2024

הרשת גועשת סביב המעבר של OpenAI מ next ל remix. כולם משתפים חוויות ומסבירים כמה גם להם היה קשה לעבוד עם next ואיך ה App Router החדש מסובך מדי. אנסה לעשות קצת סדר:

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

  2. רינדור בצד שרת (SSR) זה אפילו עוד יותר מסובך, כי יש לנו את כל הסיבוך של SPA ובנוסף צריכים להתמודד עם ניהול Cache בצד שרת ולוודא שהקוד של הקומפוננטות לא מפעיל דברים שצריכים את window כשהוא רץ בשרת. אנחנו מרנדרים בצד שרת כי מנועי חיפוש, קוראי מסך ועוד כלים רבים אחרים אוהבים לקבל את ה HTML מוכן גם אם הם לא מריצים JavaScript.

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

הנטיה של יותר מדי מפתחים היא לבחור את שיטת העבודה הכי מסובכת כי אולי נצטרך את זה בעתיד. זה מה שקרה ב OpenAI, שבונים אפליקציית SPA אבל השתמשו ב next.js שמתאים לקומפוננטות צד שרת. בשלב מסוים הם כנראה הבינו שהם משלמים יותר מדי על הבחירה הטכנולוגית שלהם ועברו לאפליקציית SPA רגילה עם רמיקס, שאגב הספיק לאחרונה להתמזג חזרה לתוך React Router.

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

ניסוי Hono - סינכרון ערך בין דפדפנים

07/09/2024

התמיכה של דינו ב Web Standards הביאה לזה שמאוד קל לכתוב קוד הונו שמשתמש ב Streams כדי לקבל הודעות מגולשים ולדווח אותן הלאה לגולשים האחרים באתר, הכל ב SSE. בואו נראה איך לחבר את החוטים בפרויקט דינו ו Hono.

המשך קריאה

זמן לקבור את זה

06/09/2024

מה עושה ראשת הצוות שמגלה שאין תשתית בחברה לבדיקות אוטומטיות למערכת? מה עושה המפתחת שמבינה שבלי אתר תיעוד אף אחד לא יבין את הפריימוורק שהיא בונה?

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

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

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

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

צוות תשתיות

05/09/2024

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

איך ההבדל הזה משפיע על הכלכלה? על התרבות? על המדע? על החיים?

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

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