• בלוג
  • עמוד 12
  • שלוש הטיות של AI שחשוב להכיר כשכותבים קוד

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

13/08/2025

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

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

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

1. העדפה לתכנות מונחה עצמים

אתגר ראשון בעבודה עם AI הוא ההעדפה שלו לגישת Object Oriented. על פניו תכנות מונחה עצמים היא גישה פופולרית בתכנות, שפות רבות משתמשות בה בתור פרדיגמה מובילה או אף יחידה ומצופה ממפתחים להכיר את הפרדיגמה.

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

דוגמה ראשונה היא Rails ו Stimulus - ביקשתי מ AI לכתוב מונה לחיצות ב Stimulus וקיבלתי את הקוד הבא:

import { Controller } from "@hotwired/stimulus"

// Connects to data-controller="click-counter"
export default class extends Controller {
  static targets = ["count"]
  static values = { count: Number }

  connect() {
    // Initialize count if not set
    if (!this.hasCountValue) {
      this.countValue = 0
    }
  }

  increment() {
    this.countValue++
  }

  countValueChanged() {
    this.countTarget.textContent = this.countValue
  }
}

מדובר בקלאס JavaScript וצריך להגיד - הקוד עובד. קלוד שמע מונה לחיצות ומיד יצר קלאס שמספר הלחיצות הוא משתנה Instance Variable בתוך הקלאס. הבעיה? הערך של מספר הלחיצות כבר כתוב ב DOM בתוך האלמנט this.countTarget. אפשר לראות אפילו בקוד שנוצר שכל מה שהוא עושה שם זה להעלות את הערך של אותו משתנה this.countValue ואז לכתוב אותו ל textContent של האלמנט.

קוד נכון וקצר יותר יהיה:

import { Controller } from "@hotwired/stimulus"

// Connects to data-controller="click-counter"
export default class extends Controller {
  static targets = ["count"]

  increment() {
    this.countTarget.textContent++
  }
}

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

2. מסובך יותר טוב מפשוט

בזן של פייתון כתוב Simple is better than Complex אבל אצל AI הסיפור הוא הפוך - איפה שאפשר לבנות פיתרון לבעיה מסובכת יותר הוא יעדיף את הפיתרון המסובך. איפה שיש קוד מסובך שפותר בעיה זה מקבל עדיפות.

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

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

    def evaluate_guess(self, guess: List[int]) -> Tuple[int, int]:
        """
        Evaluate a guess and return the number of bulls and cows.

        Args:
            guess: List of integers representing the guess

        Returns:
            Tuple of (bulls, cows) where:
            - Bulls: correct digits in correct positions
            - Cows: correct digits in wrong positions
        """
        if len(guess) != self.code_length:
            raise ValueError(f"Guess must be {self.code_length} digits long")

        if not all(self.min_digit <= digit <= self.max_digit for digit in guess):
            raise ValueError(f"All digits must be between {self.min_digit} and {self.max_digit}")

        bulls = sum(1 for i in range(self.code_length) if guess[i] == self.secret_code[i])

        # Count cows by counting occurrences of each digit in both sequences
        # and taking the minimum, then subtracting bulls
        secret_counts: dict[int, int] = {}
        guess_counts: dict[int, int] = {}

        for digit in self.secret_code:
            secret_counts[digit] = secret_counts.get(digit, 0) + 1

        for digit in guess:
            guess_counts[digit] = guess_counts.get(digit, 0) + 1

        total_matches = sum(min(secret_counts.get(digit, 0), guess_counts.get(digit, 0)) 
                           for digit in set(secret_counts.keys()) | set(guess_counts.keys()))

        cows = total_matches - bulls

        # Store the guess and result
        self.guesses.append(guess.copy())
        self.results.append((bulls, cows))

        return bulls, cows

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

def compare(guess, code):
  bulls = 0
  cows = 0

  for i in range(len(code)):
    if guess[i] == code[i]:
      cows += 1

    elif guess[i] in code:
      bulls += 1

  return (bulls, cows)

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

Count cows by counting occurrences of each digit in both sequences and taking the minimum, then subtracting bulls.

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

3. קוד ישן הוא קוד טוב

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

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

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

    const timer = setInterval(() => {
      setCurrentIndex((prevIndex) => (prevIndex + 1) % images.length)
    }, interval)

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

  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>
    </div>
  )
}

רואים את הבעיה? בואו נתמקד באפקט:

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

    const timer = setInterval(() => {
      setCurrentIndex((prevIndex) => (prevIndex + 1) % images.length)
    }, interval)

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

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

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

    const timer = setInterval(() => {
      setCurrentIndex((prevIndex) => (prevIndex + 1) % images.length)
    }, interval)

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

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

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

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

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