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

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

היום למדתי: פרטישן לפי זמן ומפתח ראשי

30/11/2025

לקחתי טבלת פוסטגרס בשביל הסיפור נניח שזו טבלת לקוחות:

CREATE TABLE customers (
    customer_id     INT PRIMARY KEY,
    full_name       TEXT NOT NULL,
    email_address   TEXT NOT NULL,
    created_at      TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at      TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

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

CREATE TABLE customers (
    customer_id     INT PRIMARY KEY,
    full_name       TEXT NOT NULL,
    email_address   TEXT NOT NULL,
    created_at      TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    updated_at      TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
)
PARTITION BY RANGE (created_at);

הוא כמובן טעה.

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

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

פתרון שנראה לי יותר טוב בתיאוריה (לא מימשתי עדיין) הוא להשתמש במפתח ראשי מסוג UUID שכולל timestamp למשל UUID7 או ULID. במפתח ראשי כזה אפשר לקחת את החלק של הזמן להיות מפתח החלוקה של הטבלה. כאן יש פוסט ארוך שמתאר את הרעיון:

https://elixirforum.com/t/partitioning-postgres-tables-by-timestamp-based-uuids/60916

בקצרה זה הקוד שלו ליצירת הטבלה:

create table(:payloads, primary_key: false, options: "PARTITION BY RANGE(id)") do
  add(:id, :binary_id, null: false, primary_key: true)

  # ... other fields and references
end

והקוד שיוצר את ה Partitions יהיה משהו כזה:

CREATE TABLE #{table}_p#{start_date.year}#{month}
PARTITION OF #{table} FOR VALUES
FROM (ulid_to_uuid('#{start_ulid}'))
TO (ulid_to_uuid('#{end_ulid}'))

התקנתי קרסר, מה עכשיו?

29/11/2025

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

המשך קריאה

שאלות ש AI לא יודע לענות עליהן

28/11/2025

למה שמת את כל הקוד בקובץ אחד?

למה בחרת את ה API הישן יותר?

למה כתבת מחדש את הפונקציה שכבר היתה כתובה במקום אחר (במקום להפוך אותה מ private ל public)?

למה שמרת את החיבור ל DB במשתנה גלובאלי ושכחת לאפס אותו וכך יצרת לי זליגת זכרון?

למה מחקת לי את הקובץ?

למה אתה מתעקש ליצור אוביקט בלולאה כשאני מתחנן שתכתוב Obect Literal?

למה לא יצרת אינדקסים על הטבלאות שיצרת ב DB?

למה יצרת קובץ locale חדש אפילו שכבר יש קובץ locale באפליקציה לכל הפיצ'רים האחרים?

למה בפונקציה אחת כתבת

setIsFlipped(!isFlipped);
if (!isFlipped) {
  setShowRating(true);
}

אבל שתי שורות למטה כתבת

const handleRate = (rating: number) => {
  onRate(rating);
  setIsFlipped(false);
  setShowRating(false);
};

(או שצריך if או שלא צריך)

למה בכפתור אחד כתבת stopPropagation אבל בכפתור השני לא?

למה אתה מושך מידע מהשרת עם useEffect ולא משתמש באיזה react-query ?

למה אתה כותב קוד שאפילו אתה לא תוכל לתחזק??


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


לכל מי שחושב ש AI הולך להחליף מפתחים Any time soon אני יכול רק להציע - שבו איתו (או אתה, זה גמיש) כמה ימים. תנו להם לכתוב קוד ותקראו את מה שיוצא. נכון, אין טעויות syntax אבל קבלת החלטות זה לא הצד החזק שלהם.

טיפ גיט: הצילו מחקתי את תיקוני הקונפליקט

27/11/2025

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

אני יוצר פרויקט עם שלושה קומיטים בשלושה ענפים, ענף main, ענף b וענף c:

* fb6d328 (HEAD -> c) c
| * fc29558 (b) b
|/
* c3482e6 (main) a

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

<<<<<<< HEAD
**The Clockmaker’s friend**
=======
**The Clockmaker’s bug**
>>>>>>> b

ענף c שנקרא HEAD מכיל את המחרוזת העליונה וענף b שאותו ניסיתי למזג מכיל את המחרוזת התחתונה ואני צריך לבחור. עד פה אין חדש. אנחנו גם יודעים לסדר את הקונפליקט, לעשות git add ואז קומיט שוב וליצור Merge Commit. אבל מה קורה אם אחרי ה add התבלבלתם והרצתם:

$ git merge --abort

פקודת merge abort מבטלת את המיזוג ומוחקת את כל השינויים המקומיים כך שהעולם חוזר להיות כמו לפני המיזוג. אבל רגע - מה קרה לתיקונים שלי? מה קרה לקונפליקט שפתרתי? הקובץ שהיה בקונפליקט עכשיו חזר להיות כמו בענף c לפני המיזוג. האם המאמץ שלי בפתרון הקונפליקט ירד לטמיון? האם אפשר לשחזר את הקובץ או שחייבים לפתור את הקונפליקט שוב?

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

git fsck --cache --no-reflogs --lost-found --unreachable HEAD

ומקבל רשימה של שטויות שהלכו לאיבוד:

unreachable tree 606ee30389b8548213f2f11596605040a54fe4e0
unreachable tree 72c93914a344c812fcf5fa7216cfd1e464bd9cc3
unreachable tree f85d50eb6572db3613432d0608b5338120883acb
unreachable blob 5c7cfc2892118a24ebbc3f8511010899e6d09781
unreachable blob dd0221c310613ea89104ce0fede8f10bf6df626a
unreachable blob 1be3a6878600782668ffaf33ff9b8843078a06cd
unreachable commit fc295586d6e98fea615ebec979850bd13b8441c3

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

$ git cat-file blob dd0221c310613ea89104ce0fede8f10bf6df626a
**The Clockmaker’s friend**
**The Clockmaker’s bug**

הבלוב dd0221c מכיל בדיוק את הקובץ אחרי ה add ולפני שזרקתי הכל עם merge --abort. כדי לקבל חזרה את הקובץ אני מריץ:

git cat-file blob dd0221c3106 > a.txt

בלי ידיים (שני קסמים)

26/11/2025

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

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

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

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

כמה לקחים לדברים אחרים שאנחנו לומדים שכדאי להשאיר בראש:

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

  2. יש קפיצות, אבל הן לא לפי הזמנה.

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

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

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

לקחים שלמדתי על עלויות

25/11/2025

  1. לתקן קוד יותר זול מלשכתב מערכת.

  2. יותר זול לבחור שפת תכנות שאני מכיר מאשר שפת תכנות שאני רוצה ללמוד.

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

  4. מונוליט יותר זול ממיקרו סרביסס.

  5. שרת יותר זול מ Serverless.

  6. מוצר מדף בתשלום הרבה פעמים יותר זול מפתרון חינמי.

  7. המחיר לשעה של פרילאנסר לא אומר הרבה על איכות הקוד.

  8. פיתוח פתרון לבעיה שאני מכיר יותר זול מפיתוח פתרון גנרי.

  9. לעבוד נכון יותר זול מלעבוד מהר.

ומה שלכם?

אתה דווקא לא נראה כמו אחד שמשתמש ב AI

24/11/2025

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

זה יעבור.

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

Real Programmers

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

להשתמש ב AI כדי לחסוך זמן אומר שה AI יכתוב לי סקריפט שמשנה מרכאות כפולות לגרש בודד בקבצי YAML, מריץ אותו על ערימה של קבצים ואז מתקן את הסקריפט כי הוא פספס כמה מקרי קצה וממשיך באיטרציה עד שכל הקבצים נכונים. כן יכולתי לכתוב את הסקריפט הזה לבד אבל יש לי דברים יותר טובים לעשות בשעה הזאת. או למפות בכל הקודבייס איזה חלקים מפעילים פונקציה מסוימת ובאיזה תנאים היא תופעל (כי יש בדרך המון if-ים). זה גם יכול להיות לקבל הסבר על פונקציה מסוימת או צורת כתיבה מסוימת במקום ללכת לחפש בגוגל ולראות מהר חלופות שיתאימו לסטנדרטים של הקוד. כמובן לקבל תקציר של שינויים ב diff מסוים ואפילו לתרגם מ Options API ל Composition API ב vue.

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

כת הקוד הקדוש

23/11/2025

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

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

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

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

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

ואם זה נכשל?

22/11/2025

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

let (feature_values, _) = features
    .append_with_names(&self.config.feature_names)
    .unwrap();

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

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

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

if save_data(items):
    report_success()

ומה קורה אם הנתונים לא נשמרו בהצלחה? "זה לא קורה אף פעם...".

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

נ.ב. זה הסיפור המלא של קלאודפלייר לסקרנים. הוא מרתק: https://blog.cloudflare.com/18-november-2025-outage/

הדברים שלא אמרתי בוובינר היום על Prompt Engineering

21/11/2025

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

אבל האמת שכל אלה הם לא הדברים החשובים.

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

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

אפשר למצוא סיכום של הנקודות המרכזיות שדיברנו עליהן בוידאו סיכום כאן: https://youtu.be/TQPZZawYW34?si=EUZWzmlW-7NYh2Z2

שבוע הבא נדבר על Cursor וגם שם יש הרבה חידושים אז שווה לקפוץ להגיד שלום. לכניסה אפשר להירשם דרך העמוד הזה:

https://www.tocode.co.il/talking_ai