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

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

חמש תקריות אבטחה הקשורות לסוכני AI שחשוב להכיר

05/04/2026

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

המשך קריאה

זהירות: תפיסת חריג מבלוק ברובי

03/04/2026

הקוד הבא ברובי זורק Exception מתוך הלולאה ותופס אותה, וקל לראות שזה עובד:

begin
  [1, 2, 3].each do |i|
    raise 'Stop' if i.even?
  end
rescue => e
  puts "Error: #{e.message}"
end

אבל חשוב לא להתבלבל, המבנה של rescue אחרי בלוק לא מבטיח שכל Exception שייזרק מהבלוק באמת ייתפס. הדוגמה הקלאסית היא קוד שנשלח להרצה ב Thread נפרד, וזה נראה ככה:

class Array
  def pmap(&block)
    each do |i|
      Thread.new { block.call(i) }
    end
    self
  end
end

begin
  [1, 2, 3].pmap do |i|
    raise 'Stop' if i.even?
  end
rescue => e
  puts "Error: #{e.message}"
end

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

class Array
  def pmap(&block)
    threads = each.map do |i|
      Thread.new do
        block.call(i)
      end
    end

    threads.each(&:join)
    threads.each(&:value)
  end
end

והפעם הפעלה תדפיס את השגיאה:

#<Thread:0x000000012233cbd8 a.rb:4 run> terminated with exception (report_on_exception is true):
a.rb:15:in `block in <main>': Stop (RuntimeError)
        from a.rb:5:in `block (2 levels) in pmap'
Error: Stop

טיפ הנדסת פרומפטים: עדיף להשתמש במה שהמודל מכיר

02/04/2026

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

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

1. **Audio Analysis**: You need to actually listen to the audio and not rely only on the existing youtube captions. YouTube transcriptions can be inaccurate and is not suitable for our requirements.

2. **Language Focus**: The provided content might include text in other languages in the beginning or the end (before or after the actual content). Only extract the main video in the clip language. You can ignore prefix or suffix if they're in another language.

3. **Phrase Segmentation Rules**:
   - **Mandatory splits**: Always split before coordinating conjunctions (and, but, or, so)
   - Split at clause boundaries (before subordinating conjunctions, after complete thoughts)
   - Keep grammatically linked words together (modal + verb, adjective + noun, etc.)
   - Target 4-7 words per phrase for optimal memorization
   - Each phrase should demonstrate one primary grammatical concept
   - Break at natural pauses that would occur in speech

אבל לא משנה מה עשיתי תמיד היו מדי פעם פספוסים.

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

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

לא הכי חדש

01/04/2026

עוד לא הספקנו להתאושש מהגרסאות המזויפות של LiteLLM וכבר התפרסם שגם axios נפרצו וגרסאות זדוניות עלו ל npm. אם בטעות תתקינו את אקסיוס 1.14.1 או 0.30.4 סקריפט שרץ אחרי התקנה יכניס סוס טרויאני שישתלט מרחוק על המחשב שלכם.

מאחר והגרסאות הזדוניות האלה לרוב מתגלות מהר הבעיה היא בעיקר של אנשים שמפעילים npm install axios בדיוק באותם יום או יומיים שהגרסה המזויפת היא החדשה ביותר על npm. מה עושים? אפשר לבדוק כל פעם ב npm מה הגרסה העדכנית, מה השינויים שהיא מביאה וכמה זמן היא באוויר ולבחור לבד גרסה אחת יותר ישנה. אפשר גם לזכור שכלי ניהול חבילות רבים מגיעים עם מנגנון Cooldown מובנה שימנע התקנת גרסאות חדשות מדי.

ב npm נוכל להשתמש בו באמצעות הפקודות הבאות בקובץ ~/.npmrc:

min-release-age=7 # days

הרבה אנשים מוסיפים גם את הפקודה:

ignore-scripts=true

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

בפייתון אין לנו עדיין פתרון מובנה ב pip אבל אם אתם משתמשים ב uv תוכלו להוסיף לקובץ ~/.config/uv/uv.toml את הפקודה:

exclude-newer = "7 days"

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

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

כל יצרן והסקילז שלו

31/03/2026

למינימקס יש ריפו של סקילז למודל שלהם: https://github.com/MiniMax-AI/skills

זה של אנטרופיק: https://github.com/anthropics/skills

זה של OpenAI: https://github.com/openai/skills

וכמובן של Gemini: https://github.com/google-gemini/gemini-skills

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

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

Copy

1. Write in product language, not design commentary.
2. Let the headline carry the meaning.
3. Supporting copy should usually be one short sentence.
4. Cut repetition between sections.
5. Do not include prompt language or design commentary into the UI.
6. Give every section one responsibility: explain, prove, deepen, or convert.

If deleting 30 percent of the copy improves the page, keep deleting.

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

Design Thinking

Purpose: What problem does this interface solve? Who uses it?
Tone: Pick an extreme: brutally minimal, maximalist chaos, retro-futuristic, organic/natural, luxury/refined, playful/toy-like, editorial/magazine, brutalist/raw, art deco/geometric, soft/pastel, industrial/utilitarian, etc. There are so many flavors to choose from. Use these for inspiration but design one that is true to the aesthetic direction.
Constraints: Technical requirements (framework, performance, accessibility).
Differentiation: What makes this UNFORGETTABLE? What's the one thing someone will remember?

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

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

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

ומי יפתח את הריאקט הבא?

30/03/2026

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

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

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

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

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

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

אני חושב שיש פה 3 תשובות אפשריות:

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

  2. נגלה בינה מלאכותית חכמה יותר מ LLM-ים והיא תמציא את הדור הבא של שפות תכנות.

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

קידוד עם AI משנה את סדר העבודה (וזה חלק מהקושי)

29/03/2026

הוספתי ל langlets אפשרות לשמור מילים באוצר מילים אישי לתרגול ומסך של תרגול המילים ששמרתי. וכשאני אומר הוספתי אני מתכוון שביקשתי מקלוד להוסיף.

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

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

https://github.com/ynonp/langlets-rails/blob/main/app/controllers/reviewlessonscontroller.rb

וזה הקובץ המקורי שטיפל במשתמשים שפשוט תרגלו את השיעור:

https://github.com/ynonp/langlets-rails/blob/main/app/controllers/lessons_controller.rb

וקל לראות שיש המון קוד משותף בין שני הקבצים האלה.

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

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

  1. תאר מה עושה קובץ א
  2. תאר מה עושה קובץ ב
  3. הסבר מהם כל התהליכים שמבוצעים בפונקציה X בקובץ א ובקובץ ב, ואיזה מהם משותפים לשני הקבצים?
  4. סדר מחדש את הקוד של פונקציה X ופצל אותו לפונקציות קטנות יותר כדי שנוכל לראות מה התפקיד של כל חלק

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

חדש בפייתון InterpreterPoolExecutor

28/03/2026

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

from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor, InterpreterPoolExecutor
import math
import time

def is_prime(n: int):
    for i in range(2, math.floor(math.sqrt(n)) + 1):
        if n % i == 0:
            return False

    return True

if __name__ == "__main__":
    t0 = time.time()
    with ThreadPoolExecutor(max_workers=4) as p:
        print(sum(p.map(is_prime, range(1_000_000))))
    t1 = time.time()

    with ProcessPoolExecutor(max_workers=4) as p:
        print(sum(p.map(is_prime, range(1_000_000))))
    t2 = time.time()

    print(f"Thread count took {t1-t0}")
    print(f"Process count took {t2 - t1}")

התוצאה כצפוי היא חישוב הרבה יותר מהיר באמצעות threads בגלל שהסינכרון הרבה יותר מהיר וגם יצירת threads הרבה יותר מהירה מיצירת Processes. אבל לשימוש ב threads יש שני חסרונות:

  1. אין הפרדה בין האוביקטים - כלומר קוד מכל threads יכול לגשת ולעדכן אוביקטים גלובאליים.

  2. גרסאות רבות של פייתון מגיעות עם Global Interpreter Lock מה שאומר שכל פעולה של פייתון בתוך ה thread צריכה לנעול את אותו מנעול גלובאלי.

גרסה 3.14 של פייתון הוסיפה פתרון ביניים שנקרא InterpreterPoolExecutor. מבצע זה יריץ כל משימה במפרש פייתון עצמאי שרץ ב thread נפרד, כלומר במקום להפעיל fork ולייצר תהליך מערכת הפעלה חדשה למפרש החדש הם מריצים את המפרש באותו תהליך. התוצאה היא הפרדה מלאה בין משתנים כי לכל מפרש יש את המשתנים הגלובאליים שלו, הפרדה בנעילות כי לכל מפרש יש את ה GIL שלו ועדיין ריצה יותר מהירה כי אנחנו רצים ב threads במקום ב processes. כמה יותר מהיר? זה מה שלקח לספור מספרים ראשוניים אצלי על המק:

Thread count took 6.0312182903289795
Process count took 80.60068678855896
Interpreter count took 24.76494789123535

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

הטעות של מפתחים שעוברים בין שפות

27/03/2026

כמה זמן באמת לוקח לעבור מפייתון ל Java? מפייתון לרובי? האם הגיוני לגייס מפתח עם נסיון ב Rust לעבודת Java או להיפך?

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

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

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

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

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

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