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

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

שלוש ביקורות שמצאתי ברשת נגד פיתוח בעזרת AI

30/04/2025

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

המשך קריאה

פייתון מתוך רובי? אין בעיה

29/04/2025

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

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

https://github.com/mrkn/pycall.rb

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

require 'pycall/import'
include PyCall::Import

pyimport :cowsay

puts cowsay.cow('Hello World')

בשביל שזה יעבוד צריך להתקין את pycall, להתקין את פייתון ולהקפיד לקמפל אותו עם האפשרות להשתמש בו בתור ספריה עם הפקודה:

env CONFIGURE_OPTS="--enable-shared" pyenv install 3.13.3

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

פיתוח משחק סנייק עם AI - מה כן עבד

28/04/2025

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

המשך קריאה

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

27/04/2025

רוצים לבלבל מועמד למשרת פייתון? שאלו אותו מה מדפיסה התוכנית הבאה:

def return_example():
    try:
        print("In try block")
        return "Try block return"
    except Exception as e:
        print(f"Exception: {e}")
    finally:
        print("In finally block")
        return "Finally block return"

result = return_example()
print(f"Result: {result}") 

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

In try block
In finally block
Result: Finally block return

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

https://peps.python.org/pep-0765/

אגב דוגמה מעניינת מה PEP של קוד אמיתי שנופל בבור הזה היתה:

try:
    ...
except:
    return NotImplemented
finally:
    return some_value

הרעיון כאן היה שמישהו רצה להחזיר ערך מסוים אם היה Exception או ערך אחר אם לא היה, אבל השימוש ב return בתוך finally ממסך על ה return של ה except וכך תמיד מוחזר הערך שמוגדר ב finally.

מיקרו החלטות, קוד שמשתכפל, למידה.

25/04/2025

הי קרסר - כתוב לי בבקשה משחק סנייק.

אין בעיה בוס, מתחילים בקובץ הזה:

import dynamic from 'next/dynamic'

// Use dynamic import to prevent hydration errors with canvas
const SnakeGame = dynamic(() => import('@/components/SnakeGame'), {
  ssr: false
})

export default function Home() {
  return (
    <main className="flex min-h-screen flex-col items-center justify-center p-4 bg-gray-100">
      <h1 className="text-3xl font-bold mb-6">Snake Game</h1>
      <SnakeGame />
    </main>
  )
}

הי קרסר הקוד לא מתקמפל - הוא מתלונן שאי אפשר לקרוא ל dynamic בלי SSR מתוך קומפוננטת צד שרת.

אין בעיה בוס אני על זה. בוא נעשה ככה:

  1. ניצור קובץ חדש בשם SnakeGameWrapper שיתחיל ב use client ונעביר אליו את כל הקוד מ page.

  2. נעדכן את page שיטען את הקובץ החדש הזה.


קרסר מסיים. הקוד עובד. אבל מי נשאר להגיד לקרסור שאפשר היה לכתוב use client פשוט בהתחלה של page.tsx במקום להוסיף קומפוננטה מיותרת? ומי יקרא את הקוד שבוע הבא ויחשוב בטעות ש page.tsx חייב להיות קומפוננטת צד שרת? ולמה בכלל משחק הסנייק צריך את dynamic? למה שלא יהיה קומפוננטת צד לקוח רגילה?

גם השמות הלא נכונים שלכם הם חוב טכני

24/04/2025

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

ואז הגיע AI.

שימו לב לשלושת הפרומפטים הכמעט זהים:

Write tests for the following python function:

def count_lines(x):
  ...
Write tests for the following python function:

def count_lines(url):
  ...
Write tests for the following python function:

def count_lines(file_path):
  ...
Write tests for the following python function:

def count_lines(fp):
  ...

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

חוב טכני והמדרון החלקלק של ה AI

23/04/2025

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

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

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

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

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

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


כמה הערות:

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

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

  3. ובנימה אישית הבלוג של סת' הוא הבלוג הוותיק ביותר שאני קורא והוא שנתן לי את ההשראה לכתוב כל יום. אני ממליץ עליו בחום.

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

https://www.youtube.com/watch?v=90WD_ats6eE

היום למדתי: דריסת הפונקציה import ב Python

22/04/2025

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

import tariff

# Set your tariff rates (package_name: percentage)
tariff.set({
    "numpy": 50,     # 50% tariff on numpy
    "pandas": 200,   # 200% tariff on pandas
    "requests": 150  # 150% tariff on requests
})

# Now when you import these packages, they'll be TARIFFED!
import numpy   # This will be 50% slower
import pandas  # This will be 200% slower

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

builtins.__import__ = _tariffed_import

זה מקור החבילה בגיטהאב:

https://github.com/hxu296/tariff/tree/main

ועכשיו אתם - מה הייתם משנים ב import? ולמה?