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

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

ללמוד כי-

17/08/2025

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

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

ולפעמים אנחנו לומדים משהו בגלל שהוא חשוב.

כשכולם היו צריכים ללמוד כל שבוע JavaScript Framework חדש היו אנשים שעצרו כדי ללמוד איך בנוי דפדפן, איך JavaScript עובדת ואיך לבנות JavaScript Framework מאפס. מה האתגרים בפיתוח Single Page Application ואיך פותרים אותם בלי Framework, איך לכתוב פיתרון Router מאפס. לא היה לידע הזה ערך מיידי. זה לא הופיע בשום רשימת דרישות במודעות דרושים ולא הופיע באף סילבוס. ובכל זאת אנשים שהשקיעו את העבודה בלימוד היסודות הצליחו ללמוד פריימוורקים חדשים הרבה יותר מהר ולעבוד נכון יותר.

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

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

היום למדתי: undefined בשאילתת פריזמה

16/08/2025

הפקודה הבאה ב prisma לא תיגמר טוב-

prisma.user.deleteMany({
  where: {
    id: undefined
  }
})

פקודת deleteMany שמקבלת undefined ב where מתעלמת מאותו בלוק where ולכן הפקודה תמחק את כל המשתמשים. זו דוגמה מהעולם האמיתי לכישלון:

const { shopId } = payload;
await prisma.event.deleteMany({
  where: { visitor: { shopId } }
});

מספיק לקבל payload בלי השדה shopId או עם שגיאת כתיב בשם השדה כדי שהבלוק ימחק את כל האירועים. מקור: https://www.ingressr.com/blog/webhook-security-incident-analysis/.

לי היתה תקלה דומה במקום אחר בשפת רובי עם like, כשבלוק כזה:

Item.where("name like '#{prefix}%-phone'").destroy_all

מחק יותר מדי רשומות כשבטעות prefix עבר בתור nil.

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

https://www.prisma.io/docs/orm/prisma-client/special-fields-and-types/null-and-undefined#strict-undefined-checks-preview-feature

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

עדכון סטייט בתוך עדכון סטייט

15/08/2025

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

useEffect(() => {
  if (images.length <= 1) return

  // Reset countdown when component mounts or when interval changes
  setCountdown(interval / 1000)

  const timer = setInterval(() => {
    setCountdown((prevCountdown) => {
      if (prevCountdown <= 1) {
        // Time to switch image
        setCurrentIndex((prevIndex) => (prevIndex + 1) % images.length)
        return interval / 1000 // Reset countdown
      }
      return prevCountdown - 1
    })
  }, 1000) // Update every second

  return () => clearInterval(timer)
}, [images.length, interval])

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

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

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

import { useState, useEffect } from 'react'
import './ImageCarousel.css'

interface ImageCarouselProps {
  images: string[]
  interval?: number
}

const ImageCarousel: React.FC<ImageCarouselProps> = ({ images, interval = 3000 }) => {
  const [elapsedTime, setElapsedTime] = useState(0)

  // Derive currentIndex and countdown from elapsedTime
  const currentCycle = Math.floor(elapsedTime / interval)
  const currentIndex = currentCycle % images.length
  const timeInCurrentCycle = elapsedTime % interval
  const countdown = Math.ceil((interval - timeInCurrentCycle) / 1000)

  useEffect(() => {
    if (images.length <= 1) return

    const timer = setInterval(() => {
      setElapsedTime((prevTime) => prevTime + 1000)
    }, 1000) // Update every second

    return () => clearInterval(timer)
  }, [images.length, interval])

  const goToSlide = (index: number) => {
    // Calculate the elapsed time that would result in the desired index
    const targetCycle = Math.floor(elapsedTime / interval) + (index - currentIndex)
    const adjustedCycle = targetCycle >= 0 ? targetCycle : targetCycle + Math.ceil(Math.abs(targetCycle) / images.length) * images.length
    setElapsedTime(adjustedCycle * interval)
  }

  const goToPrevious = () => {
    const prevIndex = currentIndex === 0 ? images.length - 1 : currentIndex - 1
    goToSlide(prevIndex)
  }

  const goToNext = () => {
    const nextIndex = (currentIndex + 1) % images.length
    goToSlide(nextIndex)
  }

  if (images.length === 0) {
    return <div className="carousel-empty">No images to display</div>
  }

  return (
    <div className="carousel-container">
      <div className="carousel-wrapper">
        <button 
          className="carousel-btn carousel-btn-prev" 
          onClick={goToPrevious}
          aria-label="Previous image"
        >
          &#8249;
        </button>

        <div className="carousel-image-container">
          <img 
            src={images[currentIndex]} 
            alt={`Slide ${currentIndex + 1}`}
            className="carousel-image"
          />
        </div>

        <button 
          className="carousel-btn carousel-btn-next" 
          onClick={goToNext}
          aria-label="Next image"
        >
          &#8250;
        </button>
      </div>

      <div className="carousel-indicators">
        {images.map((_, index) => (
          <button
            key={index}
            className={`carousel-indicator ${index === currentIndex ? 'active' : ''}`}
            onClick={() => goToSlide(index)}
            aria-label={`Go to slide ${index + 1}`}
          />
        ))}
      </div>

      {images.length > 1 && (
        <div className="carousel-countdown">
          Next image in: {countdown}s
        </div>
      )}
    </div>
  )
}

export default ImageCarousel

גם הוא עובד הפעם עם משתנה סטייט יחיד.

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

import { useState, useEffect } from 'react'
import './ImageCarousel.css'

interface CountdownTimerProps {
  interval: number
  onTimeout: () => void
}

const CountdownTimer: React.FC<CountdownTimerProps> = ({ interval, onTimeout }) => {
  const [countdown, setCountdown] = useState(interval / 1000)

  useEffect(() => {
    const timer = setInterval(() => {
      setCountdown((prevCountdown) => {
        if (prevCountdown <= 1) {
          onTimeout()
          return interval / 1000 // Reset countdown
        }
        return prevCountdown - 1
      })
    }, 1000) // Update every second

    return () => clearInterval(timer)
  }, [interval, onTimeout])

  return (
    <div className="carousel-countdown">
      Next image in: {countdown}s
    </div>
  )
}

interface ImageCarouselProps {
  images: string[]
  interval?: number
}

const ImageCarousel: React.FC<ImageCarouselProps> = ({ images, interval = 3000 }) => {
  const [currentIndex, setCurrentIndex] = useState(0)

  const goToSlide = (index: number) => {
    setCurrentIndex(index)
  }

  const goToPrevious = () => {
    setCurrentIndex((prevIndex) => 
      prevIndex === 0 ? images.length - 1 : prevIndex - 1
    )
  }

  const goToNext = () => {
    setCurrentIndex((prevIndex) => (prevIndex + 1) % images.length)
  }

  if (images.length === 0) {
    return <div className="carousel-empty">No images to display</div>
  }

  return (
    <div className="carousel-container">
      <div className="carousel-wrapper">
        <button 
          className="carousel-btn carousel-btn-prev" 
          onClick={goToPrevious}
          aria-label="Previous image"
        >
          &#8249;
        </button>

        <div className="carousel-image-container">
          <img 
            src={images[currentIndex]} 
            alt={`Slide ${currentIndex + 1}`}
            className="carousel-image"
          />
        </div>

        <button 
          className="carousel-btn carousel-btn-next" 
          onClick={goToNext}
          aria-label="Next image"
        >
          &#8250;
        </button>
      </div>

      <div className="carousel-indicators">
        {images.map((_, index) => (
          <button
            key={index}
            className={`carousel-indicator ${index === currentIndex ? 'active' : ''}`}
            onClick={() => goToSlide(index)}
            aria-label={`Go to slide ${index + 1}`}
          />
        ))}
      </div>

      {images.length > 1 && (
        <CountdownTimer 
          key={currentIndex}
          interval={interval}
          onTimeout={goToNext}
        />
      )}
    </div>
  )
}

export default ImageCarousel

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

מה דעתכם? איזה גירסה אתם הכי אוהבים ולמה?

בואו נפסיק להגיד "עדיין"

14/08/2025

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

״אתה רואה, שום AI לא יחליף אותנו בזמן הקרוב״

רק בשביל לשמוע שוב את ה ״עדיין...״.

כדאי לזכור:

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

  2. חלק משמעותי מהעבודה שלכם הוא חשיבה.

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

כן יום אחד אולי מישהו יבנה AI שיכול להבין. כתבתי על החלום הזה: https://www.tocode.co.il/blog/2025-05-emergent-is-crazy

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

שלוש הטיות של AI שחשוב להכיר כשכותבים קוד

13/08/2025

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

https://tocode.ravpage.co.il/tocodeai

עכשיו בואו נראה את עיקרי הדברים.

המשך קריאה

טיפ פייתון - הרצת תוכנית והחזרת התוצאה

12/08/2025

הפקודה eval בפייתון מריצה פקודה בודדת ומחזירה את התוצאה:

result = eval('2 + 5')

אבל בניגוד ל JavaScript, ה eval של פייתון מקבל רק פקודה אחת. זה לא עובד:

result = eval("""
import os
os.name
""")

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

ns = {}
exec("""
import os
result = os.name
""", ns)

print(ns['result'])

לסיום הרבה פעמים נרצה לעטוף את ה exec ב try/except כדי לטפל בשגיאות תחביר לדוגמה:

try:
    ns = {}
    exec("""
    import os
    result = os.name
    """, ns)

    print(ns['result'])
except SyntaxError as e:
    print(f"Syntax error: {e}")

פקודת exec יכולה לעזור כשאני רוצה לטעון קונפיגורציה מקובץ שנראה כמו פייתון לדוגמה קובץ כזה:

# settings.conf

font_face = ""
font_size = 10
line_numbers = True
tab_size = 4
auto_indent = True

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

למידע נוסף ועוד דוגמאות על exec שווה לקרוא כאן: https://realpython.com/python-exec/

אין עקומת לימוד

11/08/2025

זה מרגיש ככה עם AI לפעמים, לא?

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

עברנו הרבה מאז.

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

לא פלא שאנשים חכמים כותבים:

Learning how to use LLMs in a coding workflow is trivial

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

אבל אז הפוסט מסתיים עם התלונה:

Every time I tried using an LLM for core features of applications I develop at work, the implementations were questionable and I spent at least as much time rewriting the code than I would have spent writing it from scratch.

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

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

כדאי לזכור:

  1. גם ל AI יש עקומת לימוד.

  2. אף אחד לא יכול להחליף מתכנתים ב AI.

  3. היכרות עם היכולות, המגבלות ואופן העבודה של AI משפרת משמעותית את התוצאה שמקבלים מהכלי.

ייצוא קובץ HAR לטקסט

10/08/2025

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

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

זה הסקריפט:

import re
import json
from haralyzer import HarParser, HarPage
with open('demo.har', 'r', encoding='utf8') as f:
    har_parser = HarParser(json.loads(f.read()))

data = har_parser.har_data

for page in har_parser.pages:
    print(f"Page URL: {page.url}")

    for entry in page.entries:
        print("---\n\n")
        print("Request URL: ")
        print(entry.request.url)
        print("Request method: ")
        print(entry.request.method)
        print("Request Headers: ")
        print(entry.request.headers)
        print("")
        print("Response Headers: ")
        print(entry.response.headers)
        print("")
        if 'content' in entry.response:
            print("Response Body: ")
            print(entry.response['content'].get('text'))

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

טיפ למתקדמים - כדאי להוסיף בתחילת הלולאה דילוגים על url-ים לא רלוונטים למשל:

if entry.request.url.endswith('.jpg'): continue

סדר פריטים ב SQL

09/08/2025

כשאני מושך מבסיס נתונים את כל השירים במערכת:

select * from songs;

לפי איזה סדר הם מגיעים? האם אני מצפה לסדר? ואיך לוודא שהם יגיעו בסדר שאני צריך?

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

  2. אם יש הרבה שירים ולא אכפת לכם מהסדר, להוסיף order by יאט את השאילתה וידרוש יותר זיכרון.

ועכשיו לשאלה - האם כדאי לכתוב order by בכל שאילתה ראשית במערכת (כלומר לא במצב שה select בתוך where)?

אני מודה שאחרי שביליתי השבוע כמה שעות בשביל להבין שגיאה שנבעה משאילתה בלי order אני מתחיל לחשוב בחיוב על האופציה. מה דעתכם?

איך לקרוא את החלוקה לרמות של תומאס דומקה

08/08/2025

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

  1. סקפטיים - משתמשים ב AI למשימות קטנות או השלמת קוד (Tab Completion)

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

  3. משתפי פעולה - מפתחים שמשתמשים ב AI IDEs כמו קופיילוט או קרסר, מנסים מודלים שונים.

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

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

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

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

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

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