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

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

חידת JavaScript - למה זה מחזיר מערך?

19/05/2025

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

{a: 10}['b']

אבל מה שהודפס לא היה undefined אלא:

{a: 10}['b']
16:05:00.902 ['b']

נסו לראות אם אתם מבינים מה קרה כאן או גללו למטה לפיתרון.

המשך קריאה

חדש באתר - עוזר AI

18/05/2025

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

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

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

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

ליצור או לבחור?

17/05/2025

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

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

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

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

אין לי תשובות על השאלות האלה אבל הייתי רוצה שנשאיר אותן מול העיניים כשאנחנו משלבים AI בתוכנית הלימודים:

  1. האם אנחנו חושבים ש"ליצור" זו מיומנות חשובה ששווה לשמר (גם לבני אדם)?

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

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

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

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

https://claude.ai/public/artifacts/6c4cff06-9fe6-485d-80db-b69704f191b4

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

What's clear is that we cannot afford to let creation become a lost art. In our rush to embrace the convenience of AI tools, we must ensure that students continue to experience the productive struggle and profound satisfaction of bringing something entirely new into the world—something that began not in an algorithm, but in their own minds. The blank page may be intimidating, but it remains one of our most powerful teachers.

אני לא כל כך בטוח שהוא צודק.

רעיון, התאמה, מוצר.

16/05/2025

את הבלוק הבא ראיתי בקוד קטן ש AI יצר:

useEffect(() => {
  getWaitlistCount().then((count) => setWaitlistCount(count + 100))
}, [])

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

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

  1. הוא גורם להצגה של מידע שגוי כשהעמוד רק עולה.

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

  3. הוא מאט את זמן הרינדור בקלאיינט ומכריח את הקלאיינט להריץ JavaScript אפילו שאפשר היה להתספק רק ב HTML/CSS וקומפוננטת צד שרת.

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

כל מי שמכיר React ו Next ורואה את הקוד הזה אמור להיבהל כי זה פשוט לא הפיתרון הנכון כאן.

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

מה שיפה בעבודה עם AI הוא שאפילו את התיקון אני יכול לתת ל AI לקודד - זה הפרומפט שתיקן את הקוד:

Modify useEffect(() => { getWaitlistCount().then((count) => setWaitlistCount(count + 100)) }, []) to improve performance by moving the code to run on the server in the request handling phase and stream the response to the client by passing a promise to use. Use the result as the initial value to useState

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

נ.ב. בשבועות הקרובים אני אעלה לאתר גירסה חדשה של קורס ריאקט שתהיה מבוססת על Next ו TypeScript וגם Shadcn ו Tailwind. זה הסטאק הדיפולטי של סוכני ה AI וחשוב להכיר אותו לעומק.

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

כזה ניסיתי: דיפלוימנט עם Kamal

15/05/2025

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

המשך קריאה

אופס שברתי את next

14/05/2025

את הקוד הבא כתבתי בטעות ואני מודה שהתוצאה הפתיעה אותי:

const data = await fetch('https://api.vercel.app/blog', {
    cache: 'no-store',
    next: {
        revalidate: 60
    }
})

הקוד עבד בפיתוח אבל גרם ל build להיתקע. בואו נבין מה קורה פה.

המשך קריאה

לא סומך על ה AI

13/05/2025

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

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

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

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

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

כמה זמן צריך להשקיע ב"להבין איך זה עובד"?

12/05/2025

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

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

מה זה אומר עלינו, על המוטיבציה ללמוד ועל העתיד? כמה מחשבות:

  1. כל מי שלמד כלים רק בשביל "לגרום לדברים לעבוד" עובר או יעבור ל AI.

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

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

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

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

חדש ב node - ניקוי משאבים אוטומטי עם using

11/05/2025

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

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

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

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

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

import { open } from "node:fs/promises";

const getFileHandle = async (path: string) => {
  const fh = await open(path, "w");

  return {
    fh,
    [Symbol.asyncDispose]: async () => {
      await fh.close();
    },
  };
};

הפונקציה פותחת קובץ ומחזירה גם את הקובץ וגם את פונקציית הסגירה באוביקט אחד, כשפונקציית הסגירה נשמרת במפתח Symbol.dispose או אם היא אסינכרונית כמו בדוגמה זה יהיה Symbol.asyncDispose.

במקום אחר בקוד אנחנו יוצרים את המשאב עם המילה החדשה using:

{
  await using file = await getFileHandle("thefile.txt");
  const { fh } = file;
  fh.write('hello world');
}

וכך ביציאה מהבלוק טייפסקריפט אוטומטי יודע להפעיל את פונקציית הניקוי. קוד הטייפסקריפט בדוגמה יהפוך לקוד ה JavaScript הבא:

var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
    if (value !== null && value !== void 0) {
        if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
        var dispose, inner;
        if (async) {
            if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
            dispose = value[Symbol.asyncDispose];
        }
        if (dispose === void 0) {
            if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
            dispose = value[Symbol.dispose];
            if (async) inner = dispose;
        }
        if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
        if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
        env.stack.push({ value: value, dispose: dispose, async: async });
    }
    else if (async) {
        env.stack.push({ async: true });
    }
    return value;
};
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
    return function (env) {
        function fail(e) {
            env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
            env.hasError = true;
        }
        var r, s = 0;
        function next() {
            while (r = env.stack.pop()) {
                try {
                    if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
                    if (r.dispose) {
                        var result = r.dispose.call(r.value);
                        if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
                    }
                    else s |= 1;
                }
                catch (e) {
                    fail(e);
                }
            }
            if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
            if (env.hasError) throw env.error;
        }
        return next();
    };
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
    var e = new Error(message);
    return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
});
import { open } from "node:fs/promises";
const getFileHandle = async (path) => {
    const fh = await open(path, "w");
    return {
        fh,
        [Symbol.asyncDispose]: async () => {
            await fh.close();
        },
    };
};
{
    const env_1 = { stack: [], error: void 0, hasError: false };
    try {
        const file = __addDisposableResource(env_1, await getFileHandle("thefile.txt"), true);
        const { fh } = file;
        fh.write('hello world');
    }
    catch (e_1) {
        env_1.error = e_1;
        env_1.hasError = true;
    }
    finally {
        const result_1 = __disposeResources(env_1);
        if (result_1)
            await result_1;
    }
} // Automatically disposed!

ולמה אני מספר לכם את כל זה עכשיו? הסיבה פשוטה ובכותרת. הפיצ'ר הגיעה השבוע ל node.js ותוכלו להשתמש בו גם בלי טייפסקריפט עם node 24. אני חושב שיש פה מדיניות של node.js לתמוך בטייפסקריפט בלי לקמפל לטייפסקריפט - מדיניות שהתחילה עם מנגנון ה strip types שלהם וממשיכה עם טיפול בפקודות הטייפסקריפט שלא קשורות רק לטיפוסים. עכשיו נשאר לחכות ל enum.

על מה הלך הזמן בפרויקט Unvibing

10/05/2025

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

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

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

  3. זמן לימוד - רוב הזמן אני מבין את הקוד של ה AI אבל לפעמים הוא משתמש בתבניות או יכולות שלא הכרתי, לדוגמה בקוד JavaScript היה שימוש ב Passive Event Listeners שלא הכרתי והייתי צריך להשלים פערים.

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