📌 לפעמים קוד טוב עדיף על קוד ידידותי לסוכן
אם החלטתם על Refactor כדי שהקוד יהיה יותר ברור ואחרי השינוי אתם מגלים שגם ל AI הרבה יותר קל לבנות פיצ'רים למערכת והוא עושה פחות טעויות אין שום בעיה.
אבל מה קורה אם עשיתם את אותו Refactor וגיליתם שהוא הביא את המערכת שלכם למצב שלכם יותר קל לקרוא אותה אבל לסוכן יותר קשה?
ניסוי שהראיתי לאחרונה בקורס חשף בדיוק את התופעה הזאת:
1. הקוד החדש מכיל קבצים יותר קטנים, אפיון יותר נקי, בדיקות טובות יותר.
2. עבודה על הקוד החדש לקחה יותר טוקנים.
לא משנה כמה הוספנו הסברים ב AGENTS.md או בסקילים בסוף קלוד היה צריך לעבור על יותר קבצים, לחפש ביותר מקומות וספציפית התיקון שהיינו צריכים היה יותר קשה לביצוע עם הקוד החדש.
האם זה אומר שהריפקטור מוטעה? אני חושב שלא. כל מבנה של קוד שנבחר יגרום לפיצ'רים מסוימים להיות קלים יותר ואחרים להיות קשים יותר. זה נכון גם לקוד של בני אדם. השיקולים בבחירת ארכיטקטורה לא נגמרים ב"מה קל ל AI".
אם החלטתם על Refactor כדי שהקוד יהיה יותר ברור ואחרי השינוי אתם מגלים שגם ל AI הרבה יותר קל לבנות פיצ'רים למערכת והוא עושה פחות טעויות אין שום בעיה.
אבל מה קורה אם עשיתם את אותו Refactor וגיליתם שהוא הביא את המערכת שלכם למצב שלכם יותר קל לקרוא אותה אבל לסוכן יותר קשה?
ניסוי שהראיתי לאחרונה בקורס חשף בדיוק את התופעה הזאת:
1. הקוד החדש מכיל קבצים יותר קטנים, אפיון יותר נקי, בדיקות טובות יותר.
2. עבודה על הקוד החדש לקחה יותר טוקנים.
לא משנה כמה הוספנו הסברים ב AGENTS.md או בסקילים בסוף קלוד היה צריך לעבור על יותר קבצים, לחפש ביותר מקומות וספציפית התיקון שהיינו צריכים היה יותר קשה לביצוע עם הקוד החדש.
האם זה אומר שהריפקטור מוטעה? אני חושב שלא. כל מבנה של קוד שנבחר יגרום לפיצ'רים מסוימים להיות קלים יותר ואחרים להיות קשים יותר. זה נכון גם לקוד של בני אדם. השיקולים בבחירת ארכיטקטורה לא נגמרים ב"מה קל ל AI".
📌 לקחים מפרק ג׳וניורים תעסוקה ואפוקליפסת AI של עושים תוכנה
הקשבתי לעמית ודנה השבוע בעושים תוכנה, רוב הפרק היה מאוד מעניין ואני ממליץ להאזין. אלה הנקודות המרכזיות שאני מסכם לעצמי:
1. המרחק בין ג'וניורים לסניורים התקצר - תעשיית התוכנה אף פעם לא היתה חזקה בלתגמל נסיון (הבדיחה כשגדלתי היתה שצריך לעשות אקזיט עד גיל 40 כי אחרי זה לא תהיה לך עבודה). עמית ודנה שיתפו, וגם אני שמתי לב לזה, שהיום נסיון נהיה אפילו הרבה פחות חשוב. דברים שפעם היית צריך שנתיים כדי ללמוד אנשים מצליחים להכיר ולהיות פרודוקטיביים בהם כבר אחרי כמה שבועות. ה AI מקצר את תהליך הלמידה ומקצר את הדרך מהיכרות שטחית להכנסת קוד פרודקשן למערכות.
2. הרף עלה - לכו לקלוד ותגידו לו ליצור לכם מערכת X. התוצאה היא ה Baseline החדש. עמית אמר את זה בבירור בפרק, הוא לא היה מגייס בן אדם שחוזר אליו עם תוצאה ברמה שקלוד היה נותן. אז מה הציפייה? להיות ספציפיים, להכיר טכנולוגיה, לדחוף את ה AI לקצה, כשמגישים פיתרון להראות את עצמכם ואת האופי שלכם והבחירות שלכם בתוך קוד הפיתרון.
3. פיטורים לא תמיד קשורים אליכם - חברות קטנות מצליחות להגיע רחוק ומנכ"לים של חברות גדולות רואים את זה ומקנאים. אם המטרה שלך היא לייצר מוצרים טובים יותר, לקחת נתח שוק גדול יותר ולהרוויח יותר לא בטוח שיותר עובדים זה ייתרון. במיוחד בהינתן AI. ואני מוסיף על הדברים שלהם שעוד אין לנו פרדיגמה טובה איך לשתף פעולה על פרויקט תוכנה בין מספר בני אדם וסוכני קידוד. ככל שמהירות הפיתוח עולה אנחנו מבזבזים יותר זמן על תקשורת ותיאומים ונוצר מצב שלפעמים להוסיף מפתח שני לפרויקט כבר מאט את הפרויקט משמעותית.
מה עושים? אין תשובות טובות. כנראה לאף אחד. בינתיים אפשר לשים לב לשאלות, ללמוד איך ליצור תוכנה טוב יותר בעולם של ה AI וללמוד למצוא את הערך הייחודי שלנו שאנחנו מביאים לכל פרויקט. הצורך בתרומה אנושית רק מתחזק ככל שסוכני הבינה משתלבים בחיינו.
הקשבתי לעמית ודנה השבוע בעושים תוכנה, רוב הפרק היה מאוד מעניין ואני ממליץ להאזין. אלה הנקודות המרכזיות שאני מסכם לעצמי:
1. המרחק בין ג'וניורים לסניורים התקצר - תעשיית התוכנה אף פעם לא היתה חזקה בלתגמל נסיון (הבדיחה כשגדלתי היתה שצריך לעשות אקזיט עד גיל 40 כי אחרי זה לא תהיה לך עבודה). עמית ודנה שיתפו, וגם אני שמתי לב לזה, שהיום נסיון נהיה אפילו הרבה פחות חשוב. דברים שפעם היית צריך שנתיים כדי ללמוד אנשים מצליחים להכיר ולהיות פרודוקטיביים בהם כבר אחרי כמה שבועות. ה AI מקצר את תהליך הלמידה ומקצר את הדרך מהיכרות שטחית להכנסת קוד פרודקשן למערכות.
2. הרף עלה - לכו לקלוד ותגידו לו ליצור לכם מערכת X. התוצאה היא ה Baseline החדש. עמית אמר את זה בבירור בפרק, הוא לא היה מגייס בן אדם שחוזר אליו עם תוצאה ברמה שקלוד היה נותן. אז מה הציפייה? להיות ספציפיים, להכיר טכנולוגיה, לדחוף את ה AI לקצה, כשמגישים פיתרון להראות את עצמכם ואת האופי שלכם והבחירות שלכם בתוך קוד הפיתרון.
3. פיטורים לא תמיד קשורים אליכם - חברות קטנות מצליחות להגיע רחוק ומנכ"לים של חברות גדולות רואים את זה ומקנאים. אם המטרה שלך היא לייצר מוצרים טובים יותר, לקחת נתח שוק גדול יותר ולהרוויח יותר לא בטוח שיותר עובדים זה ייתרון. במיוחד בהינתן AI. ואני מוסיף על הדברים שלהם שעוד אין לנו פרדיגמה טובה איך לשתף פעולה על פרויקט תוכנה בין מספר בני אדם וסוכני קידוד. ככל שמהירות הפיתוח עולה אנחנו מבזבזים יותר זמן על תקשורת ותיאומים ונוצר מצב שלפעמים להוסיף מפתח שני לפרויקט כבר מאט את הפרויקט משמעותית.
מה עושים? אין תשובות טובות. כנראה לאף אחד. בינתיים אפשר לשים לב לשאלות, ללמוד איך ליצור תוכנה טוב יותר בעולם של ה AI וללמוד למצוא את הערך הייחודי שלנו שאנחנו מביאים לכל פרויקט. הצורך בתרומה אנושית רק מתחזק ככל שסוכני הבינה משתלבים בחיינו.
👍1
📌 שבתות וחגים - מה קורה כשהמודל מנסה יותר מדי
נתתי ל Sol את הפרומפט הבא כדי לבנות תוכנית פייתון שמדלגת על שבתות וחגים, למשל בשביל לא לשלוח מייל או לחייב לקוחות בשבת:
התוצאה היתה מרשימה. זה קובץ ה calendar.py שהוא יצר:
נתתי ל Sol את הפרומפט הבא כדי לבנות תוכנית פייתון שמדלגת על שבתות וחגים, למשל בשביל לא לשלוח מייל או לחייב לקוחות בשבת:
create a new python project with uv
the app is a cron scheduler we should start it via a CLI
cron.py cronfile
cronfile is in the same format as any regular crontab EXCEPT it has a twist
each line also has a boolean indicating if the request should execute in Jewish holidays and Saturdays
(called "skip_shabatot_vehagim")
If skip_shabatot_vehagim is set that line shouldn't execute on Jewish holidays and Saturdays with religious date
boundaries
התוצאה היתה מרשימה. זה קובץ ה calendar.py שהוא יצר:
"""Protected intervals, starting the evening before each sacred civil date."""
import math
from dataclasses import dataclass
from datetime import UTC, date, datetime, timedelta
from functools import lru_cache
from zoneinfo import ZoneInfo
from astral import Observer, SunDirection
from astral.sun import sunset, time_at_elevation
from pyluach.dates import HebrewDate
class BoundaryError(ValueError):
"""The location has no calculable sunset or nightfall for a required date."""
@dataclass(frozen=True)
class ReligiousCalendar:
timezone: ZoneInfo = ZoneInfo("Asia/Jerusalem")
latitude: float = 31.778
longitude: float = 35.235
israel: bool = True
start_offset_minutes: float = 0
nightfall_degrees: float = 8.5
nightfall_minutes: float | None = None
def __post_init__(self) -> None:
for name, value, lower, upper in (
("latitude", self.latitude, -90, 90),
("longitude", self.longitude, -180, 180),
("start offset", self.start_offset_minutes, 0, 180),
("nightfall degrees", self.nightfall_degrees, 1, 30),
):
if not math.isfinite(value) or not lower <= value <= upper:
raise ValueError(f"{name} must be between {lower} and {upper}")
if self.nightfall_minutes is not None and (
not math.isfinite(self.nightfall_minutes)
or not 0 < self.nightfall_minutes <= 240
):
raise ValueError("nightfall minutes must be greater than 0 and at most 240")
@lru_cache(maxsize=1024) # noqa: B019 - bounded cache of immutable configurations
def protected_date(self, day: date) -> bool:
if day.weekday() == 5:
return True
hebrew = HebrewDate.from_pydate(day)
# Pyluach numbers Nissan as 1 and Tishrei as 7. Only holidays on
# which work is prohibited are included, rather than minor holidays.
sacred = {
(7, 1),
(7, 2), # Rosh Hashanah (two days also in Israel)
(7, 10), # Yom Kippur
(7, 15),
(7, 22), # Sukkot and Shemini Atzeret / Simchat Torah
(1, 15),
(1, 21), # First and last days of Pesach
(3, 6), # Shavuot
}
if not self.israel:
sacred |= {(7, 16), (7, 23), (1, 16), (1, 22), (3, 7)}
return (hebrew.month, hebrew.day) in sacred
@lru_cache(maxsize=1024) # noqa: B019 - bounded cache of immutable configurations
def interval(self, day: date) -> tuple[datetime, datetime]:
observer = Observer(self.latitude, self.longitude)
try:
start = sunset(observer, day - timedelta(days=1), self.timezone)
start = start.astimezone(UTC) - timedelta(minutes=self.start_offset_minutes)
if self.nightfall_minutes is not None:
end = sunset(observer, day, self.timezone).astimezone(UTC)
end += timedelta(minutes=self.nightfall_minutes)
else:
end = time_at_elevation(
observer,
-self.nightfall_degrees,
day,
SunDirection.SETTING,
self.timezone,
with_refraction=False,
).astimezone(UTC)
❤1
except ValueError as error:
raise BoundaryError(
f"cannot calculate religious boundaries for {day}"
) from error
if end <= start:
raise BoundaryError(f"invalid religious boundaries for {day}")
return start, end
def is_protected(self, when: datetime) -> bool:
if when.tzinfo is None or when.utcoffset() is None:
raise ValueError(
"religious boundary checks require a timezone-aware datetime"
)
local_day = when.astimezone(self.timezone).date()
instant = when.astimezone(UTC)
# Yesterday covers a nightfall after midnight at high latitudes.
# Tomorrow covers Friday and holiday-eve evenings. Intersecting
# intervals naturally keep consecutive holy days protected.
for delta in (-1, 0, 1):
day = local_day + timedelta(days=delta)
if self.protected_date(day):
start, end = self.interval(day)
if start <= instant < end:
return True
return False
הקובץ כולל את רשימת החגים מובנית, המרה לתאריך עברי ואפילו בדיקת שעות כדי לא לבצע משימות בערב חג אבל כן לאפשר משימות אחרי צאת שבת.
אם הייתי נותן ל AI להריץ איטרציית שיפור על הקוד הוא כנראה היה מבין שאין צורך להגדיר את רשימת החגים בתוך הפונקציה
protected_date ואפשר להוציא אותה למשתנה של המודול כדי לשפר ביצועים ולקצר קצת את הקוד.ובכל זאת הייתי נזהר מלהשתמש בקוד כזה במערכת שלי. הקוד גבולי מדי, הוא חושב שהוא יודע הכל ועושה מאמץ למצוא בדיוק את שעת השקיעה כדי לזהות מתי נכנסת שבת. המאמץ לעשות את הדבר הנכון בא על חשבון מרווחי ביטחון בריאים.
תיקון טוב במקרה הזה היה הפרומפט:
replace sunset calculation with a healthy safe buffer
זה הקובץ אחרי התיקון:
"""Conservative local-clock buffers around Shabbat and Jewish holidays."""
from dataclasses import dataclass
from datetime import UTC, date, datetime, time, timedelta
from functools import lru_cache
from zoneinfo import ZoneInfo
from pyluach.dates import HebrewDate
@dataclass(frozen=True)
class ReligiousCalendar:
timezone: ZoneInfo = ZoneInfo("Asia/Jerusalem")
israel: bool = True
protection_start: time = time(15, 0)
protection_end: time = time(23, 0)
def __post_init__(self) -> None:
for boundary in (self.protection_start, self.protection_end):
if boundary.tzinfo is not None or boundary.second or boundary.microsecond:
raise ValueError("protection times must be local HH:MM times")
if self.protection_end < self.protection_start:
raise ValueError("protection end must not be earlier than protection start")
@lru_cache(maxsize=1024) # noqa: B019 - bounded cache of immutable configurations
def protected_date(self, day: date) -> bool:
if day.weekday() == 5:
return True
hebrew = HebrewDate.from_pydate(day)
# Pyluach numbers Nissan as 1 and Tishrei as 7. Only holidays on
# which work is prohibited are included, rather than minor holidays.
sacred = {
(7, 1),
(7, 2), # Rosh Hashanah (two days also in Israel)
(7, 10), # Yom Kippur
(7, 15),
(7, 22), # Sukkot and Shemini Atzeret / Simchat Torah
(1, 15),
(1, 21), # First and last days of Pesach
(3, 6), # Shavuot
}
if not self.israel:
sacred |= {(7, 16), (7, 23), (1, 16), (1, 22), (3, 7)}
return (hebrew.month, hebrew.day) in sacred
@lru_cache(maxsize=1024) # noqa: B019 - bounded cache of immutable configurations
def interval(self, day: date) -> tuple[datetime, datetime]:
start = datetime.combine(
day - timedelta(days=1), self.protection_start, self.timezone
)
end = datetime.combine(day, self.protection_end, self.timezone)
return start.astimezone(UTC), end.astimezone(UTC)
def is_protected(self, when: datetime) -> bool:
if when.tzinfo is None or when.utcoffset() is None:
raise ValueError(
"religious boundary checks require a timezone-aware datetime"
)
local_day = when.astimezone(self.timezone).date()
instant = when.astimezone(UTC)
# Tomorrow covers the buffered eve. Overlapping intervals keep
# consecutive holy days continuously protected.
for delta in (0, 1):
day = local_day + timedelta(days=delta)
if self.protected_date(day):
start, end = self.interval(day)
if start <= instant < end:
return True
return False
גם קוד פשוט יותר וגם עונה טוב יותר למטרה האמיתית של הפרומפט הראשון.
אז מה זה אומר שמפתחים עוברים להיות אנשי Product ומאפיינים? סוג של. מה שחשוב היום בעבודה של מפתחים הוא ראייה מערכתית. להבין איך המערכת צריכה להיראות, איזה מנגנונים בקוד אנחנו רוצים לבנות ואיפה אנחנו רוצים דווקא לוותר על המנגנון המתוחכם בשביל משהו פשוט שיהיה יותר יציב. להבין איך הקוד מתחבר לדרישות ואיך לבחור את המימוש שיענה טוב יותר גם על הדרישות שלא נמצאות בפרומפט.
📌 סנטינל בפייתון 3.15
פייתון 3.15 יצא ואחרי החגיגות וההתקנות הלכתי לקרוא את רשימת השינויים. חלק מהם ממש מעולים כמו האפשרות לייבא עם lazy כדי לשפר את זמן העלייה של הסקריפט, המעבר ל UTF-8 כברירת מחדל, מילון קפוא וכמובן הפרופיילר החדש שדוגם חיצונית את התוכנית במקום לבדוק מתוך הסקריפט כמו שעשינו בעבר.
ויחד איתם פיצ'ר אחד בשם sentinel שאני בכלל לא בטוח שעדיין נחוץ.
סנטינל, או בעברית זקיף או שומר, הוא ערך מיוחד שנועד לתאר את המצב בו הפונקציה שלנו לא יכולה להחזיר מה שביקשו ממנה. לדוגמה נכתוב פונקציה לחיפוש האינדקס הראשון בו מופיע פריט מסוים ברשימה:
אם אני מחפש והפריט נמצא ברשימה הפונקציה תחזיר את האינדקס שלו. אם אני מחפש והפריט אינו ברשימה היא תחזיר None:
אבל מה קורה אם None הוא דווקא ערך שיכול להיות מוחזר גם באופן רגיל מהפונקציה? במצב כזה נהיה בבעיה כי לא נדע להבדיל בין המצב שפונקציה מחזירה None בתור מצד רגיל למצב בו היא מחזירה None רק כי אין לה משהו אחר להחזיר.
דוגמה טובה היא הפונקציה next של פייתון שמחזירה את הדבר הבא מאיטרטור:
קריאה ל next אחרי שהאיטרטור נגמר זורקת חריג בשם StopIteration כי אין לו יותר מה להחזיר. הפונקציה next עצמה לא יכולה להחזיר זקיף כי כל ערך שנבחר היה יכול להופיע באופן רגיל בערך ההחזר שלה.
אגב next עצמה מאפשרת למי שקורא לה לבחור את הזקיף וכך מתמודדת עם הבעיה:
כך שאם אתם כן צריכים את הכתיבה בלי ה Exception יש פיתרון.
ומה לגבי sentinel? בעיקר לא מזיק:
הזקיף הוא ערך מיוחד שאנחנו יכולים להחזיר כדי לסמן לעצמנו שאין ערך. כל פעם שמפעילים sentinel מקבלים ערך מיוחד חדש אפילו אם הטקסט שמתאר אותו הוא זהה. זקיף שורד העתקות וגם השמה למשתנים נוספים וגם pickling:
אז מה הבעיה עם הפיצ'ר? אני חושב שהוא פשוט מיותר:
1. המנגנונים הקיימים כמו להחזיר None או לזרוק שגיאה מכסים את רוב המקרים.
2. אם בכל זאת חייבים ערך מיוחד שיהיה שונה מ None אפשר להגדיר לבד class.
בעולם שבו סוכנים יכתבו את רוב הקוד הייתי שמח לראות פחות פיצ'רים בשפות ויותר אפשרויות להרחיב את השפות עם מנגנונים שאנחנו (כלומר הסוכן שלנו) נכתוב.
פייתון 3.15 יצא ואחרי החגיגות וההתקנות הלכתי לקרוא את רשימת השינויים. חלק מהם ממש מעולים כמו האפשרות לייבא עם lazy כדי לשפר את זמן העלייה של הסקריפט, המעבר ל UTF-8 כברירת מחדל, מילון קפוא וכמובן הפרופיילר החדש שדוגם חיצונית את התוכנית במקום לבדוק מתוך הסקריפט כמו שעשינו בעבר.
ויחד איתם פיצ'ר אחד בשם sentinel שאני בכלל לא בטוח שעדיין נחוץ.
סנטינל, או בעברית זקיף או שומר, הוא ערך מיוחד שנועד לתאר את המצב בו הפונקציה שלנו לא יכולה להחזיר מה שביקשו ממנה. לדוגמה נכתוב פונקציה לחיפוש האינדקס הראשון בו מופיע פריט מסוים ברשימה:
def first_index(needle, haystack):
for index, item in enumerate(haystack):
if item == needle:
return index
אם אני מחפש והפריט נמצא ברשימה הפונקציה תחזיר את האינדקס שלו. אם אני מחפש והפריט אינו ברשימה היא תחזיר None:
print(first_index('a', ['x', 'y', 'a', 'b', 'a']))
2
print(first_index('foo', ['x', 'y', 'a', 'b', 'a']))
Noneאבל מה קורה אם None הוא דווקא ערך שיכול להיות מוחזר גם באופן רגיל מהפונקציה? במצב כזה נהיה בבעיה כי לא נדע להבדיל בין המצב שפונקציה מחזירה None בתור מצד רגיל למצב בו היא מחזירה None רק כי אין לה משהו אחר להחזיר.
דוגמה טובה היא הפונקציה next של פייתון שמחזירה את הדבר הבא מאיטרטור:
>>> items = iter(['a', 'b', 'c'])
>>> print(next(items))
a
>>> print(next(items))
b
>>> print(next(items))
c
>>> print(next(items))
Traceback (most recent call last):
File "<python-input-4>", line 1, in <module>
print(next(items))
~~~~^^^^^^^
StopIteration
קריאה ל next אחרי שהאיטרטור נגמר זורקת חריג בשם StopIteration כי אין לו יותר מה להחזיר. הפונקציה next עצמה לא יכולה להחזיר זקיף כי כל ערך שנבחר היה יכול להופיע באופן רגיל בערך ההחזר שלה.
אגב next עצמה מאפשרת למי שקורא לה לבחור את הזקיף וכך מתמודדת עם הבעיה:
>>> items = iter(['a', 'b', 'c'])
>>> print(next(items, None))
a
>>> print(next(items, None))
b
>>> print(next(items, None))
c
>>> print(next(items, None))
None
>>> print(next(items, None))
None
>>> print(next(items, None))
None
>>>
כך שאם אתם כן צריכים את הכתיבה בלי ה Exception יש פיתרון.
ומה לגבי sentinel? בעיקר לא מזיק:
>>> items = iter(['a', 'b', 'c'])
>>> missing = sentinel('MISSING')
>>> print(next(items, missing))
a
>>> print(next(items, missing))
b
>>> print(next(items, missing))
c
>>> print(next(items, missing))
MISSING
>>> print(next(items, missing))
MISSING
>>> print(next(items, missing))
MISSING
>>> next(items, missing) is missing
True
>>>
הזקיף הוא ערך מיוחד שאנחנו יכולים להחזיר כדי לסמן לעצמנו שאין ערך. כל פעם שמפעילים sentinel מקבלים ערך מיוחד חדש אפילו אם הטקסט שמתאר אותו הוא זהה. זקיף שורד העתקות וגם השמה למשתנים נוספים וגם pickling:
>>> x = sentinel('x')
>>> y = sentinel('x')
>>> z = x
>>> x is x
True
>>> x is z
True
>>> x is y
False
>>>אז מה הבעיה עם הפיצ'ר? אני חושב שהוא פשוט מיותר:
1. המנגנונים הקיימים כמו להחזיר None או לזרוק שגיאה מכסים את רוב המקרים.
2. אם בכל זאת חייבים ערך מיוחד שיהיה שונה מ None אפשר להגדיר לבד class.
בעולם שבו סוכנים יכתבו את רוב הקוד הייתי שמח לראות פחות פיצ'רים בשפות ויותר אפשרויות להרחיב את השפות עם מנגנונים שאנחנו (כלומר הסוכן שלנו) נכתוב.
📌 סנטינל בפייתון 3.15
פייתון 3.15 יצא ואחרי החגיגות וההתקנות הלכתי לקרוא את רשימת השינויים. חלק מהם ממש מעולים כמו האפשרות לייבא עם lazy כדי לשפר את זמן העלייה של הסקריפט, המעבר ל UTF-8 כברירת מחדל, מילון קפוא וכמובן הפרופיילר החדש שדוגם חיצונית את התוכנית במקום לבדוק מתוך הסקריפט כמו שעשינו בעבר.
ויחד איתם פיצ'ר אחד בשם sentinel שאני בכלל לא בטוח שעדיין נחוץ.
סנטינל, או בעברית זקיף או שומר, הוא ערך מיוחד שנועד לתאר את המצב בו הפונקציה שלנו לא יכולה להחזיר מה שביקשו ממנה. לדוגמה נכתוב פונקציה לחיפוש האינדקס הראשון בו מופיע פריט מסוים ברשימה:
אם אני מחפש והפריט נמצא ברשימה הפונקציה תחזיר את האינדקס שלו. אם אני מחפש והפריט אינו ברשימה היא תחזיר None:
אבל מה קורה אם None הוא דווקא ערך שיכול להיות מוחזר גם באופן רגיל מהפונקציה? במצב כזה נהיה בבעיה כי לא נדע להבדיל בין המצב שפונקציה מחזירה None בתור מצד רגיל למצב בו היא מחזירה None רק כי אין לה משהו אחר להחזיר.
דוגמה טובה היא הפונקציה next של פייתון שמחזירה את הדבר הבא מאיטרטור:
קריאה ל next אחרי שהאיטרטור נגמר זורקת חריג בשם StopIteration כי אין לו יותר מה להחזיר. הפונקציה next עצמה לא יכולה להחזיר זקיף כי כל ערך שנבחר היה יכול להופיע באופן רגיל בערך ההחזר שלה.
אגב next עצמה מאפשרת למי שקורא לה לבחור את הזקיף וכך מתמודדת עם הבעיה:
כך שאם אתם כן צריכים את הכתיבה בלי ה Exception יש פיתרון.
ומה לגבי sentinel? בעיקר לא מזיק:
הזקיף הוא ערך מיוחד שאנחנו יכולים להחזיר כדי לסמן לעצמנו שאין ערך. כל פעם שמפעילים sentinel מקבלים ערך מיוחד חדש אפילו אם הטקסט שמתאר אותו הוא זהה. זקיף שורד העתקות וגם השמה למשתנים נוספים וגם pickling:
אז מה הבעיה עם הפיצ'ר? אני חושב שהוא פשוט מיותר:
1. המנגנונים הקיימים כמו להחזיר None או לזרוק שגיאה מכסים את רוב המקרים.
2. אם בכל זאת חייבים ערך מיוחד שיהיה שונה מ None אפשר להגדיר לבד class.
בעולם שבו סוכנים יכתבו את רוב הקוד הייתי שמח לראות פחות פיצ'רים בשפות ויותר אפשרויות להרחיב את השפות עם מנגנונים שאנחנו (כלומר הסוכן שלנו) נכתוב.
פייתון 3.15 יצא ואחרי החגיגות וההתקנות הלכתי לקרוא את רשימת השינויים. חלק מהם ממש מעולים כמו האפשרות לייבא עם lazy כדי לשפר את זמן העלייה של הסקריפט, המעבר ל UTF-8 כברירת מחדל, מילון קפוא וכמובן הפרופיילר החדש שדוגם חיצונית את התוכנית במקום לבדוק מתוך הסקריפט כמו שעשינו בעבר.
ויחד איתם פיצ'ר אחד בשם sentinel שאני בכלל לא בטוח שעדיין נחוץ.
סנטינל, או בעברית זקיף או שומר, הוא ערך מיוחד שנועד לתאר את המצב בו הפונקציה שלנו לא יכולה להחזיר מה שביקשו ממנה. לדוגמה נכתוב פונקציה לחיפוש האינדקס הראשון בו מופיע פריט מסוים ברשימה:
def first_index(needle, haystack):
for index, item in enumerate(haystack):
if item == needle:
return index
אם אני מחפש והפריט נמצא ברשימה הפונקציה תחזיר את האינדקס שלו. אם אני מחפש והפריט אינו ברשימה היא תחזיר None:
print(first_index('a', ['x', 'y', 'a', 'b', 'a']))
2
print(first_index('foo', ['x', 'y', 'a', 'b', 'a']))
Noneאבל מה קורה אם None הוא דווקא ערך שיכול להיות מוחזר גם באופן רגיל מהפונקציה? במצב כזה נהיה בבעיה כי לא נדע להבדיל בין המצב שפונקציה מחזירה None בתור מצד רגיל למצב בו היא מחזירה None רק כי אין לה משהו אחר להחזיר.
דוגמה טובה היא הפונקציה next של פייתון שמחזירה את הדבר הבא מאיטרטור:
>>> items = iter(['a', 'b', 'c'])
>>> print(next(items))
a
>>> print(next(items))
b
>>> print(next(items))
c
>>> print(next(items))
Traceback (most recent call last):
File "<python-input-4>", line 1, in <module>
print(next(items))
~~~~^^^^^^^
StopIteration
קריאה ל next אחרי שהאיטרטור נגמר זורקת חריג בשם StopIteration כי אין לו יותר מה להחזיר. הפונקציה next עצמה לא יכולה להחזיר זקיף כי כל ערך שנבחר היה יכול להופיע באופן רגיל בערך ההחזר שלה.
אגב next עצמה מאפשרת למי שקורא לה לבחור את הזקיף וכך מתמודדת עם הבעיה:
>>> items = iter(['a', 'b', 'c'])
>>> print(next(items, None))
a
>>> print(next(items, None))
b
>>> print(next(items, None))
c
>>> print(next(items, None))
None
>>> print(next(items, None))
None
>>> print(next(items, None))
None
>>>
כך שאם אתם כן צריכים את הכתיבה בלי ה Exception יש פיתרון.
ומה לגבי sentinel? בעיקר לא מזיק:
>>> items = iter(['a', 'b', 'c'])
>>> missing = sentinel('MISSING')
>>> print(next(items, missing))
a
>>> print(next(items, missing))
b
>>> print(next(items, missing))
c
>>> print(next(items, missing))
MISSING
>>> print(next(items, missing))
MISSING
>>> print(next(items, missing))
MISSING
>>> next(items, missing) is missing
True
>>>
הזקיף הוא ערך מיוחד שאנחנו יכולים להחזיר כדי לסמן לעצמנו שאין ערך. כל פעם שמפעילים sentinel מקבלים ערך מיוחד חדש אפילו אם הטקסט שמתאר אותו הוא זהה. זקיף שורד העתקות וגם השמה למשתנים נוספים וגם pickling:
>>> x = sentinel('x')
>>> y = sentinel('x')
>>> z = x
>>> x is x
True
>>> x is z
True
>>> x is y
False
>>>אז מה הבעיה עם הפיצ'ר? אני חושב שהוא פשוט מיותר:
1. המנגנונים הקיימים כמו להחזיר None או לזרוק שגיאה מכסים את רוב המקרים.
2. אם בכל זאת חייבים ערך מיוחד שיהיה שונה מ None אפשר להגדיר לבד class.
בעולם שבו סוכנים יכתבו את רוב הקוד הייתי שמח לראות פחות פיצ'רים בשפות ויותר אפשרויות להרחיב את השפות עם מנגנונים שאנחנו (כלומר הסוכן שלנו) נכתוב.
📌 עזוב את ה AI ותתחיל לתכנת
האם זו עצה טובה למתחילים?
נתחיל עם מה שאנחנו יודעים:
1. יש מיומנויות שנשמרות ויש שנשכחות. אנחנו לא שוכחים לרכב על אופניים אבל כן שוכחים איך לכתוב קוד. גם לפני ה AI מפתחים שיצאו לחופשה של כמה חודשים התקשו עם משימות קידוד אפילו פשוטות והיו צריכים תקופת הסתגלות. גם לפני ה AI מפתחים היו צריכים להתאמן לראיונות עבודה ולפתור שוב ושוב את אותם תרגילי ליטקוד.
2. יש מפתחים שעבדו שנים על מערכת ועדיין מכירים רק חלק קטן או ממוקד ממנה בלי להבין את התמונה הגדולה. גם זה קרה הרבה לפני ה AI. כתיבת קוד ידנית היא לא ערובה להיכרות עמוקה עם מערכת תוכנה וכל התהליכים בה.
3. היכרות טובה עם כל שלבי סולם האבסטרקציה עוזרת לאנשים להבין טוב יותר את השלב בו הם נמצאים אבל לא מבטיחה הצלחה. מפתחים שמבינים איך חומרה עובדת, יודעים לקרוא אסמבלי, מכירים את שפת C ויודעים לקרוא את הקוד של הדפדפן יוכלו להבין את הפרטים של פיתוח ווב טוב יותר מחבריהם שעבדו רק בתוך דפדפן, אבל גם פה פגשתי מפתחי ווב טובים משני הסוגים.
אז למה אנחנו חושבים שהיכרות טובה עם שפת תכנות ויכולת כתיבת קוד הכרחיות או לפחות תורמות לבניית מערכות טובות בעידן ה AI? אני חושב שהסיפור הוא פחות הידע הספציפי בשפת תכנות מסוימת ויותר היכולת לעבוד עם אבסטרקציות, לחשוב ולפתור בעיות בתוך המערכת.
מפתחים שיודעים לממש מיון במערך מתוך סט של פעולות פשוטות הם בעלי סיכוי טוב יותר לארגן מערכת מיקרו סרביסים מבוזרת מתוך סט הפעולות הפשוטות שספק הענן נותן להם. את שפת פייתון עצמה אנחנו שוכחים; המיומנות של עבודה דרך אבסטרקציות או בניית מבנים מורכבים מתוך רכיבים פשוטים יותר נשארת איתנו ומתחזקת עם השנים.
ובחזרה לשאלה מהכותרת: "עזוב את ה AI ותתחיל לתכנת" היא עצה שמכוונת למטרה הנכונה דרך הכלי הלא נכון. הבעיה אף פעם לא הייתה מי מקליד את הקוד, אלא מי מקבל את ההחלטות. מתחיל שנותן לסוכן גם לכתוב את הקוד וגם להחליט איך המערכת בנויה לא ילמד הרבה. אבל גם מתחיל שמקליד כל שורה בעצמו ומעתיק את המבנה מטוטוריאל לא ילמד הרבה יותר.
אנחנו לא צריכים להתרחק מסוכני קידוד כדי להתחיל לחשוב. להיפך, סוכני הקידוד יכולים להיות חברים טובים שלנו וללמד אותנו לחשוב. הם יכולים להכריח אותנו לקבל החלטות, לבחון אותנו על ההחלטות האלה ולעמת אותנו עם מקרים שלא צפינו והתפתחויות עתידיות של המערכת שלא תכננו. ההקלדה לא הייתה אף פעם החלק החשוב.
האם זו עצה טובה למתחילים?
נתחיל עם מה שאנחנו יודעים:
1. יש מיומנויות שנשמרות ויש שנשכחות. אנחנו לא שוכחים לרכב על אופניים אבל כן שוכחים איך לכתוב קוד. גם לפני ה AI מפתחים שיצאו לחופשה של כמה חודשים התקשו עם משימות קידוד אפילו פשוטות והיו צריכים תקופת הסתגלות. גם לפני ה AI מפתחים היו צריכים להתאמן לראיונות עבודה ולפתור שוב ושוב את אותם תרגילי ליטקוד.
2. יש מפתחים שעבדו שנים על מערכת ועדיין מכירים רק חלק קטן או ממוקד ממנה בלי להבין את התמונה הגדולה. גם זה קרה הרבה לפני ה AI. כתיבת קוד ידנית היא לא ערובה להיכרות עמוקה עם מערכת תוכנה וכל התהליכים בה.
3. היכרות טובה עם כל שלבי סולם האבסטרקציה עוזרת לאנשים להבין טוב יותר את השלב בו הם נמצאים אבל לא מבטיחה הצלחה. מפתחים שמבינים איך חומרה עובדת, יודעים לקרוא אסמבלי, מכירים את שפת C ויודעים לקרוא את הקוד של הדפדפן יוכלו להבין את הפרטים של פיתוח ווב טוב יותר מחבריהם שעבדו רק בתוך דפדפן, אבל גם פה פגשתי מפתחי ווב טובים משני הסוגים.
אז למה אנחנו חושבים שהיכרות טובה עם שפת תכנות ויכולת כתיבת קוד הכרחיות או לפחות תורמות לבניית מערכות טובות בעידן ה AI? אני חושב שהסיפור הוא פחות הידע הספציפי בשפת תכנות מסוימת ויותר היכולת לעבוד עם אבסטרקציות, לחשוב ולפתור בעיות בתוך המערכת.
מפתחים שיודעים לממש מיון במערך מתוך סט של פעולות פשוטות הם בעלי סיכוי טוב יותר לארגן מערכת מיקרו סרביסים מבוזרת מתוך סט הפעולות הפשוטות שספק הענן נותן להם. את שפת פייתון עצמה אנחנו שוכחים; המיומנות של עבודה דרך אבסטרקציות או בניית מבנים מורכבים מתוך רכיבים פשוטים יותר נשארת איתנו ומתחזקת עם השנים.
ובחזרה לשאלה מהכותרת: "עזוב את ה AI ותתחיל לתכנת" היא עצה שמכוונת למטרה הנכונה דרך הכלי הלא נכון. הבעיה אף פעם לא הייתה מי מקליד את הקוד, אלא מי מקבל את ההחלטות. מתחיל שנותן לסוכן גם לכתוב את הקוד וגם להחליט איך המערכת בנויה לא ילמד הרבה. אבל גם מתחיל שמקליד כל שורה בעצמו ומעתיק את המבנה מטוטוריאל לא ילמד הרבה יותר.
אנחנו לא צריכים להתרחק מסוכני קידוד כדי להתחיל לחשוב. להיפך, סוכני הקידוד יכולים להיות חברים טובים שלנו וללמד אותנו לחשוב. הם יכולים להכריח אותנו לקבל החלטות, לבחון אותנו על ההחלטות האלה ולעמת אותנו עם מקרים שלא צפינו והתפתחויות עתידיות של המערכת שלא תכננו. ההקלדה לא הייתה אף פעם החלק החשוב.
❤1
📌 מה זה טעם, למה צריך אחד ואיך ללמוד את זה
כשאין מה להגיד אנחנו ממציאים מילים - לדוגמה "taste" היא מילה שאמורה לתאר את איכות הקוד שאינה ניתנת לכימות.
מי שיש לו טעם טוב יודע להגיד ל AI "לא זה עדיין לא שם, הבדיקה צריכה להיות יותר רחבה" או "רגע רגע תחליף את זה בבדיקה יותר מדויקת".
בעידן שבו סוכני קידוד כותבים את הקוד הגיוני לשאול:
1. למה צריך "טעם"? למי אכפת איך המערכת נראית?
2. איך מפתחים אותו?
נתחיל עם הצורך. יש עדיין הסכמה בתעשייה שאנחנו רוצים לגייס אנשים שמבינים מערכות ואנחנו מעדיפים שאנשים עם הבנה טכנית יהיו אחראים על המוצרים שלנו (גם אם אנחנו לוחצים עליהם רוב הזמן להעלות פיצ'רים בלי לקרוא מספיק לעומק את הקוד). טאריק יורט המחיש את זה יפה בפוסט כאן:
https://mtyurt.net/post/2026/we-should-still-send-home-assignments-in-engineering-interviews
שמסביר על מבחני בית. טאריק משתף שבשבילו משימת בית למועמדים היא דלת שנפתחת לערימה של שאלות כמו:
1. למה בחרת דווקא את הספריה הזו? האם היו התלבטויות או אפשרויות רלוונטיות אחרות? באיזה מצבים היית בוחר בחלופה אחרת?
2. ראיתי שהקוד שלך מורכב משני מודולים. לפי מה החלטת על החלוקה ביניהם? למה דווקא שם? מה היה גורם לחלוקה אחרת?
3. מהם צווארי הבקבוק בארכיטקטורה שיצרת? למה בחרת למקם אותם שם? מה היה משנה את החלטתך?
ראיתם איך טאריק הפך את המשחק משאלון על "איך המערכת עובדת" לשאלון בחירה ואחריות? המשחק הוא לא להבין מה קרה אלא לבחור מה לצייר. הבחירה היא הטעם.
מלבד הצורך להשתלב בתעשייה יש גם עניין של המערכת עצמה. סוכני קידוד משכפלים סגנון כתיבה שהם רואים ומשתמשים ברכיבים בסיסיים שהם כבר מוצאים בקוד. מפתחים שמקפידים שהקוד יראה טוב מגבירים את הסיכוי שסוכן הקידוד שלהם יכתוב קוד שנראה טוב.
השאלה השנייה לדעתי יותר קשה - איך מפתחים טעם? אם אני מסתכל על קוד בדיקה לדוגמה קוד כזה:
הבעיות כאן עדינות, אבל משמעותיות. ה assert שבודק אם המחרוזת נמצאת בתוך התוצאה (במקום לבדוק התאמה מלאה של התוצאה) משאיר מקום לטעויות אבל יותר מזה מייצר "תבנית" לבדיקות. מכאן הסוכן ימשיך להשתמש בהשוואת in כי זה קל וזה מה שנמצא בכל הבדיקות. הבחירה לבדוק את הפונקציה הפרטית מאלצת כל שינוי עתידי בה להתמודד עם הבדיקה שאולי תכשל ושוב מייצרת מבנה שישתכפל לפונקציות הבאות.
הבעיה שזה נכון לכל פונקציה ולכל שינוי בקוד. כשאני רואה שינוי אני יכול להבין או להרגיש כשהשינוי הזה לא מספיק טוב. כשאני מתמקד בבדיקה מסוימת אני יכול להצביע על הבעיות שבה וגם כשאני אדביק אותה ל AI לשיחה הבעיות האלה יעלו. ככל שמסתכלים על שינוי יותר גדול שמשפיע על יותר פונקציות יותר קשה לעצור ולהתלבט.
לכן הפיתרון שלי ל"איך ללמוד טעם" הוא דווקא להתחיל מקטן. להתחיל מקטן כי זה הכי קל והכי זמין ושינויים קטנים מצטברים. בדוגמה של פונקציית הבדיקה שהדבקתי כאן אם נתקן את שתי הבעיות נגלה שדרך התיקון אנחנו בונים כלים יותר מדויקים לכתיבת תוכניות בדיקה כמו פונקציות עזר מסוימות. דרך כתיבתם אנחנו מבינים טוב יותר איך הקוד צריך להיראות.
דרך נוספת היא לקרוא קוד וללמוד תיאוריה של קוד. כן לדעת מה זה Dependency Injection ומה היתרונות והחסרונות של Delegation ולהכיר את התבנית של Generator ו yield ובכלל Design Patterns. תיאוריה של קוד שנראתה אולי מעייפת לפני 5 שנים היא היום הלחם והחמאה של המקצוע.
כשאין מה להגיד אנחנו ממציאים מילים - לדוגמה "taste" היא מילה שאמורה לתאר את איכות הקוד שאינה ניתנת לכימות.
מי שיש לו טעם טוב יודע להגיד ל AI "לא זה עדיין לא שם, הבדיקה צריכה להיות יותר רחבה" או "רגע רגע תחליף את זה בבדיקה יותר מדויקת".
בעידן שבו סוכני קידוד כותבים את הקוד הגיוני לשאול:
1. למה צריך "טעם"? למי אכפת איך המערכת נראית?
2. איך מפתחים אותו?
נתחיל עם הצורך. יש עדיין הסכמה בתעשייה שאנחנו רוצים לגייס אנשים שמבינים מערכות ואנחנו מעדיפים שאנשים עם הבנה טכנית יהיו אחראים על המוצרים שלנו (גם אם אנחנו לוחצים עליהם רוב הזמן להעלות פיצ'רים בלי לקרוא מספיק לעומק את הקוד). טאריק יורט המחיש את זה יפה בפוסט כאן:
https://mtyurt.net/post/2026/we-should-still-send-home-assignments-in-engineering-interviews
שמסביר על מבחני בית. טאריק משתף שבשבילו משימת בית למועמדים היא דלת שנפתחת לערימה של שאלות כמו:
1. למה בחרת דווקא את הספריה הזו? האם היו התלבטויות או אפשרויות רלוונטיות אחרות? באיזה מצבים היית בוחר בחלופה אחרת?
2. ראיתי שהקוד שלך מורכב משני מודולים. לפי מה החלטת על החלוקה ביניהם? למה דווקא שם? מה היה גורם לחלוקה אחרת?
3. מהם צווארי הבקבוק בארכיטקטורה שיצרת? למה בחרת למקם אותם שם? מה היה משנה את החלטתך?
ראיתם איך טאריק הפך את המשחק משאלון על "איך המערכת עובדת" לשאלון בחירה ואחריות? המשחק הוא לא להבין מה קרה אלא לבחור מה לצייר. הבחירה היא הטעם.
מלבד הצורך להשתלב בתעשייה יש גם עניין של המערכת עצמה. סוכני קידוד משכפלים סגנון כתיבה שהם רואים ומשתמשים ברכיבים בסיסיים שהם כבר מוצאים בקוד. מפתחים שמקפידים שהקוד יראה טוב מגבירים את הסיכוי שסוכן הקידוד שלהם יכתוב קוד שנראה טוב.
השאלה השנייה לדעתי יותר קשה - איך מפתחים טעם? אם אני מסתכל על קוד בדיקה לדוגמה קוד כזה:
def test_html_href_preserves_mailto_and_tel_links() -> None:
html = (
'<p>Contact <a href="mailto:team@example.com">the team</a> or '
'<a href="tel:+1234567890">call us</a>.</p>'
)
markdown = _convert_html(html)
assert "[the team](mailto:team@example.com)" in markdown
assert "[call us](tel:+1234567890)" in markdown
הבעיות כאן עדינות, אבל משמעותיות. ה assert שבודק אם המחרוזת נמצאת בתוך התוצאה (במקום לבדוק התאמה מלאה של התוצאה) משאיר מקום לטעויות אבל יותר מזה מייצר "תבנית" לבדיקות. מכאן הסוכן ימשיך להשתמש בהשוואת in כי זה קל וזה מה שנמצא בכל הבדיקות. הבחירה לבדוק את הפונקציה הפרטית מאלצת כל שינוי עתידי בה להתמודד עם הבדיקה שאולי תכשל ושוב מייצרת מבנה שישתכפל לפונקציות הבאות.
הבעיה שזה נכון לכל פונקציה ולכל שינוי בקוד. כשאני רואה שינוי אני יכול להבין או להרגיש כשהשינוי הזה לא מספיק טוב. כשאני מתמקד בבדיקה מסוימת אני יכול להצביע על הבעיות שבה וגם כשאני אדביק אותה ל AI לשיחה הבעיות האלה יעלו. ככל שמסתכלים על שינוי יותר גדול שמשפיע על יותר פונקציות יותר קשה לעצור ולהתלבט.
לכן הפיתרון שלי ל"איך ללמוד טעם" הוא דווקא להתחיל מקטן. להתחיל מקטן כי זה הכי קל והכי זמין ושינויים קטנים מצטברים. בדוגמה של פונקציית הבדיקה שהדבקתי כאן אם נתקן את שתי הבעיות נגלה שדרך התיקון אנחנו בונים כלים יותר מדויקים לכתיבת תוכניות בדיקה כמו פונקציות עזר מסוימות. דרך כתיבתם אנחנו מבינים טוב יותר איך הקוד צריך להיראות.
דרך נוספת היא לקרוא קוד וללמוד תיאוריה של קוד. כן לדעת מה זה Dependency Injection ומה היתרונות והחסרונות של Delegation ולהכיר את התבנית של Generator ו yield ובכלל Design Patterns. תיאוריה של קוד שנראתה אולי מעייפת לפני 5 שנים היא היום הלחם והחמאה של המקצוע.
mtyurt.net
We Should Still Send Home Assignments in Engineering Interviews
I’ve been hiring software engineers for the last 8 years. I’ve reviewed hundreds of home assignments as part of hiring process. In the recently introduced AI era, it is more and more believed that hiring assignments are useless because candidate can just…
📌 המבורגר בלי בצל
האנלוגיה הבאה של ריץ' היקי קולעת לנקודה שהרבה אנשים מרגישים:
בגדול מה שהוא אומר זה שבעבודה עם AI אנחנו לא לומדים מיומנות אלא לומדים "לעבוד על" הכלי. במקום להשקיע בקידוד אנחנו מייצרים טכניקות שאמורות לגרום למכונה לייצר קוד טוב, בלי להבין מה זה בכלל אומר קוד טוב. הוא משווה את זה ללקוח ברשת בורגרים שמבקש המבורגר בלי בצל רק בשביל להכריח את הרשת להכין לו אחד טרי ולא לתת אחד שהכינו כבר לפני שעתיים.
הביקורת הזו קולעת בדיוק להצעה של דוד בוב ושל רבים אחרים בתעשייה - לפיה אם רק נבנה מנגנונים מספיק טובים לא נצטרך לקרוא קוד. נוכל להחליף את הבנתנו את המערכת בהבנה של מנגנוני הבקרה ושיפור איכות הקוד וכך נשפר את מהירות העבודה. בזה אני מסכים גם. החלום של אנשים רבים להפסיק לראות קוד לדעתי לא יתגשם בעזרת AI ולולאות משוב.
מצד שני לא הייתי זורק את כל ה AI מתחת לגלגלי משאית ה"אין מיומנות". כמפתח שקורא את הקוד אני יודע לראות עליית מדרגה בסולם האבסטרקציה ודחיפה לחשוב על ארכיטקטורה ומקרי קצה הרבה יותר מבעבר. למעשה בעבודה עם AI חשיבה, תכנון, ארכיטקטורה ומקרי קצה הם כל מה שנשאר. התוצאה, למי שמוכנים לקרוא את הקוד, לאתגר את עצמם ואת ה AI ולדרוש מהסוכן את המערכת שהם תכננו, היא פיתוח מערכות טובות ואמינות יותר.
אנחנו בנקודת החלטה. המצב הנוכחי לא יציב ועדיין מוקדם לדעת איך עולם התוכנה יראה בעוד שלוש שנים כשהאבק ישקע. מה שבטוח הוא שאני שמח לקרוא אנשים שמתעקשים להישאר מקצועיים וקרובים לקוד. יש לי תחושה שהמקצועיות תמשיך להשתלם.
האנלוגיה הבאה של ריץ' היקי קולעת לנקודה שהרבה אנשים מרגישים:
What you are doing while using automation, instead of learning real-world truths, is attempting to divine the details du jour of some automaton. And striving to game them - e.g. ordering a custom burger (no onions) in order to get a freshly made one. Or filling voodoo.md with just the right incantations. You are only a software update from everything you've 'learned' being rendered obsolete by the latest algorithm. (A soggy cold burger from which the onions have been removed) You have traded working with the world to working for a capricious robot.
בגדול מה שהוא אומר זה שבעבודה עם AI אנחנו לא לומדים מיומנות אלא לומדים "לעבוד על" הכלי. במקום להשקיע בקידוד אנחנו מייצרים טכניקות שאמורות לגרום למכונה לייצר קוד טוב, בלי להבין מה זה בכלל אומר קוד טוב. הוא משווה את זה ללקוח ברשת בורגרים שמבקש המבורגר בלי בצל רק בשביל להכריח את הרשת להכין לו אחד טרי ולא לתת אחד שהכינו כבר לפני שעתיים.
הביקורת הזו קולעת בדיוק להצעה של דוד בוב ושל רבים אחרים בתעשייה - לפיה אם רק נבנה מנגנונים מספיק טובים לא נצטרך לקרוא קוד. נוכל להחליף את הבנתנו את המערכת בהבנה של מנגנוני הבקרה ושיפור איכות הקוד וכך נשפר את מהירות העבודה. בזה אני מסכים גם. החלום של אנשים רבים להפסיק לראות קוד לדעתי לא יתגשם בעזרת AI ולולאות משוב.
מצד שני לא הייתי זורק את כל ה AI מתחת לגלגלי משאית ה"אין מיומנות". כמפתח שקורא את הקוד אני יודע לראות עליית מדרגה בסולם האבסטרקציה ודחיפה לחשוב על ארכיטקטורה ומקרי קצה הרבה יותר מבעבר. למעשה בעבודה עם AI חשיבה, תכנון, ארכיטקטורה ומקרי קצה הם כל מה שנשאר. התוצאה, למי שמוכנים לקרוא את הקוד, לאתגר את עצמם ואת ה AI ולדרוש מהסוכן את המערכת שהם תכננו, היא פיתוח מערכות טובות ואמינות יותר.
אנחנו בנקודת החלטה. המצב הנוכחי לא יציב ועדיין מוקדם לדעת איך עולם התוכנה יראה בעוד שלוש שנים כשהאבק ישקע. מה שבטוח הוא שאני שמח לקרוא אנשים שמתעקשים להישאר מקצועיים וקרובים לקוד. יש לי תחושה שהמקצועיות תמשיך להשתלם.
👍1
📌 סיכום וובינר: התקנת אפליקציה על שרת שלנו
בוובינר השבוע ראינו איך להתקין אפליקציה על שרת שלנו בעזרת AI. אם הייתם בוובינר ראיתם שבחרתי בהתחלה שרת חלש של 6$ לחודש ב Digital Ocean אבל אותו שרת לא הצליח להתמודד עם הרצת AI והתקנות ורק לקראת סוף הוובינר החלפנו לשרת חדש יותר כך שלא הצלחנו לראות את כל תהליך ההתקנה. למען הסדר הטוב אחרי הוובינר הקלטתי גרסת וידאו ערוכה של כל התהליך עם שרת חזק. זה יושב ביוטיוב ואני ממליץ לצפות אם הנושא מעניין אתכם בקישור כאן:
https://youtu.be/SN621Dk-26Y
האפליקציה שהותקנה היא משחק פקמן והקוד שלו זמין בגיטהאב כאן:
https://github.com/ynonp/manypacman
להלן הנקודות המרכזיות מהוידאו.
✏ למה להתקין שרת שלכם
שאלה ראשונה שלדעתי חייבת התיחסות היא למה בכלל לטרוח. בעולם של עננים ובמיוחד לאפליקציית next.js אפשר לקבל את האפליקציה באוויר בלחיצת כפתור דרך vercel, אז בשביל מה לטרוח להקים שרת? אלה הסיבות שלי:
1. כיף - התקנת שרת זה קודם כל כיף. זה שרת שלכם, אפשר לשים עליו מה שרוצים ואנחנו לא מוגבלים למה שפלטפורמה מסוימת מאפשרת או לדרך עבודה מסוימת.
2. למידה - הקמת שרת שלכם תלמד אתכם על שרתים, מערכות וארכיטקטורה.
3. מחיר - ברור באפליקציית ניסוי השרת עולה יותר מאחסון בחינם על vercel או קלאודפלייר, אבל ככל שיש יותר אפליקציות עם יותר תנועה שרתים שלנו יכולים לחסוך עלויות.
✏ איך זה עובד
ברגע שזה שרת שלנו יש המון אופציות להתקנה והפעלה של המערכת. בוידאו הראיתי את המבנה הבא:
1. האפליקציה שמורה על השרת בתיקיית הבית של משתמש שמתאים לה.
2. לאפליקציה יש service ב systemd שמנטר אותה ואם יש התרסקות או בעיה מפעיל מחדש.
3. האפליקציה מקשיבה על פורט 3000 אבל חיבורים חיצוניים לא מגיעים ישירות ל next. יש שרת nginx שמקבל את כל החיבורים ומעביר אותם ל next. שרת ה nginx גם יטפל בדומיינים ובתעודות.
4. כל פעם שמשנים קוד ומעלים גרסה חדשה לגיטהאב מופעל Github Action שמתחבר לשרת, מבצע pull, בונה ומפעיל מחדש את הסרביס.
אלטרנטיבה שהעלינו בוובינר היתה שימוש ב docker. ה Github Action בונה image ושירות שרץ על השרת מנטר את מאגר האימג'ים ומושך גרסה חדשה כשהיא עולה.
✏ הקמה בעידן ה AI
אחרי החלטה על שיטת עבודה ההקמה עצמה לא דורשת היכרות עם תחביר של הכלים כלל. אין צורך לזכור את תחביר הקונפיגורציה של nginx, של Github Actions, של systemd או אפילו פקודות יוניקס שיוצרות משתמש.
במקום זה התחברנו לשרת, הפעלנו opencode (גירסה חינמית לא דורש אפילו רישום) וביקשנו להתקין את כל המערכת שתיארתי. במהלך הוובינר והוידאו הסתכנו על הפקודות שהסוכן הריץ כדי להבין כי זה חלק מהכיף אבל לא היינו צריכים לתקן אותו או לכוון מעבר למה שהוסבר כאן.
גם אחרי שחיברתי דומיין חזרתי לאותו opencode בשביל שייקח מ letsencrypt תעודות ויחבר את הדומיין לשרת והכל נוצר בצורה אוטומטית.
אין ספק שעידן ה AI מאפשר סוג של "טייס אוטומטי" להמון משימות בעולם התוכנה. כשמבינים איך המערכת עובדת ומוודאים שקיבלנו את מה שרצינו הדבר יכול לחסוך המון זמן. שימו לב רק שאבטחת מידע היא לא הצד החזק שלו - בוידאו הראיתי בדיוק איך למרות שביקשתי שהמערכת תרוץ ממשתמש שאינו מנהל מערכת, בפועל המשתמש שנוצר כן נוצר עם הרשאות ניהול מלאות. אולי זה מה שקורה כשעובדים עם מודלים חינמיים. אולי פשוט עדיין צריך לשמור על עירנות.
בוובינר השבוע ראינו איך להתקין אפליקציה על שרת שלנו בעזרת AI. אם הייתם בוובינר ראיתם שבחרתי בהתחלה שרת חלש של 6$ לחודש ב Digital Ocean אבל אותו שרת לא הצליח להתמודד עם הרצת AI והתקנות ורק לקראת סוף הוובינר החלפנו לשרת חדש יותר כך שלא הצלחנו לראות את כל תהליך ההתקנה. למען הסדר הטוב אחרי הוובינר הקלטתי גרסת וידאו ערוכה של כל התהליך עם שרת חזק. זה יושב ביוטיוב ואני ממליץ לצפות אם הנושא מעניין אתכם בקישור כאן:
https://youtu.be/SN621Dk-26Y
האפליקציה שהותקנה היא משחק פקמן והקוד שלו זמין בגיטהאב כאן:
https://github.com/ynonp/manypacman
להלן הנקודות המרכזיות מהוידאו.
✏ למה להתקין שרת שלכם
שאלה ראשונה שלדעתי חייבת התיחסות היא למה בכלל לטרוח. בעולם של עננים ובמיוחד לאפליקציית next.js אפשר לקבל את האפליקציה באוויר בלחיצת כפתור דרך vercel, אז בשביל מה לטרוח להקים שרת? אלה הסיבות שלי:
1. כיף - התקנת שרת זה קודם כל כיף. זה שרת שלכם, אפשר לשים עליו מה שרוצים ואנחנו לא מוגבלים למה שפלטפורמה מסוימת מאפשרת או לדרך עבודה מסוימת.
2. למידה - הקמת שרת שלכם תלמד אתכם על שרתים, מערכות וארכיטקטורה.
3. מחיר - ברור באפליקציית ניסוי השרת עולה יותר מאחסון בחינם על vercel או קלאודפלייר, אבל ככל שיש יותר אפליקציות עם יותר תנועה שרתים שלנו יכולים לחסוך עלויות.
✏ איך זה עובד
ברגע שזה שרת שלנו יש המון אופציות להתקנה והפעלה של המערכת. בוידאו הראיתי את המבנה הבא:
1. האפליקציה שמורה על השרת בתיקיית הבית של משתמש שמתאים לה.
2. לאפליקציה יש service ב systemd שמנטר אותה ואם יש התרסקות או בעיה מפעיל מחדש.
3. האפליקציה מקשיבה על פורט 3000 אבל חיבורים חיצוניים לא מגיעים ישירות ל next. יש שרת nginx שמקבל את כל החיבורים ומעביר אותם ל next. שרת ה nginx גם יטפל בדומיינים ובתעודות.
4. כל פעם שמשנים קוד ומעלים גרסה חדשה לגיטהאב מופעל Github Action שמתחבר לשרת, מבצע pull, בונה ומפעיל מחדש את הסרביס.
אלטרנטיבה שהעלינו בוובינר היתה שימוש ב docker. ה Github Action בונה image ושירות שרץ על השרת מנטר את מאגר האימג'ים ומושך גרסה חדשה כשהיא עולה.
✏ הקמה בעידן ה AI
אחרי החלטה על שיטת עבודה ההקמה עצמה לא דורשת היכרות עם תחביר של הכלים כלל. אין צורך לזכור את תחביר הקונפיגורציה של nginx, של Github Actions, של systemd או אפילו פקודות יוניקס שיוצרות משתמש.
במקום זה התחברנו לשרת, הפעלנו opencode (גירסה חינמית לא דורש אפילו רישום) וביקשנו להתקין את כל המערכת שתיארתי. במהלך הוובינר והוידאו הסתכנו על הפקודות שהסוכן הריץ כדי להבין כי זה חלק מהכיף אבל לא היינו צריכים לתקן אותו או לכוון מעבר למה שהוסבר כאן.
גם אחרי שחיברתי דומיין חזרתי לאותו opencode בשביל שייקח מ letsencrypt תעודות ויחבר את הדומיין לשרת והכל נוצר בצורה אוטומטית.
אין ספק שעידן ה AI מאפשר סוג של "טייס אוטומטי" להמון משימות בעולם התוכנה. כשמבינים איך המערכת עובדת ומוודאים שקיבלנו את מה שרצינו הדבר יכול לחסוך המון זמן. שימו לב רק שאבטחת מידע היא לא הצד החזק שלו - בוידאו הראיתי בדיוק איך למרות שביקשתי שהמערכת תרוץ ממשתמש שאינו מנהל מערכת, בפועל המשתמש שנוצר כן נוצר עם הרשאות ניהול מלאות. אולי זה מה שקורה כשעובדים עם מודלים חינמיים. אולי פשוט עדיין צריך לשמור על עירנות.
❤1🔥1