• בלוג
  • שתי הרמות של הקוד

שתי הרמות של הקוד

26/05/2026

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

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

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

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

write a script to fix the SRT timestamps based on transcript.txt assembly ai times. Walk both files in parallel ignoring small differences and fix timestamps in the SRT based on the
 transcript.txt word timestamps

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

בנקודה הזאת עצרתי את ה AI והוספתי הודעת הכוונה:

Better -
start with the SRT, first line in the SRT includes N words
take these N words from the list of word-timestamps transcript.txt and use the first timestamp
Then continue iterating SRT lines

when a word doesn't match you can skip it, but if multiple words don't match (maybe 3) you should backtrack to check if a word was just skipped (missing)
if you can't match or backtrack raise an exception as there's a problem with either the script or the data

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

מה ש AI כן יכול לעשות זה לכתוב את אותה לולאת פייתון לפי ההוראות שכתבתי, בסיפור שלנו הפונקציה נראית כך:

def align_and_fix(
    srt_entries: list[dict], ts_words: list[dict]
) -> list[dict]:
    """
    Align SRT entries with transcript word timestamps.
    Returns the entries list with new_start_ms / new_end_ms set.
    """
    # Flatten SRT words with entry tracking
    srt_flat = []  # (norm_word, entry_index)
    for entry in srt_entries:
        for w in entry["norm_words"]:
            srt_flat.append((w, entry["index"]))

    ts_norms = [w["norm"] for w in ts_words]

    sp = 0
    tp = 0
    entry_ts_map: dict[int, list[int]] = {}

    while sp < len(srt_flat) and tp < len(ts_norms):
        srt_word, ei = srt_flat[sp]

        if words_match(srt_word, ts_norms[tp]):
            entry_ts_map.setdefault(ei, []).append(tp)
            sp += 1
            tp += 1
        else:
            offset = find_sync(
                [w[0] for w in srt_flat], ts_norms, sp, tp
            )
            if offset is not None:
                tp += offset
            else:
                # Try skipping SRT words
                found = False
                for sahead in range(1, MAX_SKIPS + 1):
                    if sp + sahead < len(srt_flat):
                        if words_match(srt_flat[sp + sahead][0], ts_norms[tp]):
                            sp += sahead
                            found = True
                            break
                if not found:
                    sp += 1
                    tp += 1

    # Compute timestamps per entry
    results = []
    for entry in srt_entries:
        ts_indices = entry_ts_map.get(entry["index"], [])
        if ts_indices:
            new_start = ts_words[ts_indices[0]]["ms"]
            last_idx = ts_indices[-1]
            if last_idx + 1 < len(ts_words):
                new_end = ts_words[last_idx + 1]["ms"]
            else:
                new_end = ts_words[last_idx]["ms"] + 500
        else:
            new_start = None
            new_end = None
        results.append((entry["index"], new_start, new_end))

    # Interpolate gaps
    for i, (idx, start, end) in enumerate(results):
        if start is None:
            prev_match = next(
                (results[j] for j in range(i - 1, -1, -1) if results[j][1] is not None),
                None,
            )
            next_match = next(
                (results[j] for j in range(i + 1, len(results)) if results[j][1] is not None),
                None,
            )
            if prev_match and next_match:
                old_prev = srt_entries[prev_match[0] - 1]["old_start_ms"]
                old_next = srt_entries[next_match[0] - 1]["old_start_ms"]
                old_curr = srt_entries[idx - 1]["old_start_ms"]
                if old_next != old_prev:
                    frac = (old_curr - old_prev) / (old_next - old_prev)
                else:
                    frac = 0.5
                interp_start = int(prev_match[1] + frac * (next_match[1] - prev_match[1]))
                interp_end = int(prev_match[2] + frac * (next_match[2] - prev_match[2]))
                results[i] = (idx, interp_start, interp_end)
            elif prev_match:
                results[i] = (idx, prev_match[1], prev_match[2])
            elif next_match:
                results[i] = (idx, next_match[1], next_match[2])

    return results

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

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

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