תפקיד המתכנת בעידן הבינה המלאכותית שיוצרת קוד

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

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

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

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

אני אמחיש באמצעות דוגמה קטנה (כל הדוגמאות הן מ GitHub copilot, code whisper או GPT4 דרך הגרסה בתשלום). הייתי צריך לייצר פונקצית תמצות שיוצרת האש בפייתון. זה מה שקיבלתי:

import hashlib

def hash_string(input_string):
    # Create a sha256 hash object
    hash_object = hashlib.sha256()
    
    # Update the hash object with the bytes-like object (the string, encoded)
    hash_object.update(input_string.encode())
    
    # Get the hexadecimal representation of the digest
    hex_dig = hash_object.hexdigest()
    
    return hex_dig

# Example usage
input_string = "Hello, world!"
hashed_string = hash_string(input_string)
print(f"Original String: {input_string}")
print(f"Hashed String: {hashed_string}")

הקוד הזה עובד ובאמת מעולה. האם יש פה בעיית אבטחה ופרטיות? התשובה היא כמובן חד משמעית: תלוי. תלוי בשימוש. מי שיש לו ידע או ניסיון בקריפטוגרפיה יגיד שיש סיכוי שהבינה המלאכותית טעתה פה – כי היא לא הוסיפה salt. טקסט מג׳ונרט שנועד למנוע שינוי. חלק אחר (יותר קטן) יגידו שיש כאן טעות כי לא הוצע שימוש כפול בתמצות שנפוץ במקרים מסוימים. חלק אחר יעיר, ובצדק, שיש פונקציות תמצות מומלצות יותר לססמאות.

צודקים, אבל טועים. בקונטקסט מסוים SHA-256 הוא מעולה והקוד הזה מצוין ועובד היטב. בקונטקסט אחר הוא לא טוב. ד״ר ארז ויסברד שעובד איתי אמר לי, בתשובה לשאלה שלי בנושא שפונקצית תמצות זה כמו כובע מצחיה – לחבוש כובע כשאתה מסתובב בחוץ ביום חם זה רעיון מצוין. מיותר כשאתה בבית במזגן ואסון כאשר אתה נכנס איתו למסעדת יוקרה עם קוד לבוש. כלומר הכל תלוי בקונטקסט. הקוד הזה טוב? לא טוב? תלוי איפה אני משתמש בו.

אתן עוד דוגמה. ביקשתי ליצור קוד שבודק ולידיות של כתובת מייל. השאילתה שלי היתה Create a JavaScript code that checks if the email address is valid. זו התשובה שקיבלתי:

function validateEmail(email) {
    // Regular expression for basic email validation
    const regex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;

    return regex.test(String(email).toLowerCase());
}

// Example usage
const email1 = "[email protected]";
console.log(`${email1}: ${validateEmail(email1)}`); // Should log: [email protected]: true

const email2 = "not-an-email";
console.log(`${email2}: ${validateEmail(email2)}`); // Should log: not-an-email: false

הקוד הזה עובד, תקין ולגיטימי. א-ב-ל מתכנתים עם יותר ניסיון יעקמו את האף. הם יגידו שביטוי רגולרי, במיוחד לבדיקת מייל, הוא פוטנציאל לנזק ועדיף להשתמש בספריות ולידציה. אם אני משתמש בו בצד הלקוח, יגידו וגם בצדק שעדיף מכל הבחינות להשתמש ב-type=email ב-input הישר ב-HTML ולקבל מהקופסה בדיקת אימייל שגם נגישה, עובדת ובלי קוד בכלל. אבל – האם הקוד הזה רע? הקוד שלעיל עובד, אין בו בעיית אבטחה, אין בו שום דבר רע – אם אני מממש אותו למשל בצד שרת, ב-POC, במקום שבו אני לא יכול להשתמש ב-dependency של ספרית ולידציה או אפילו במיקרו בקר (כן! עם ג׳אווהסקריפט) הוא יהיה מעולה, אבל במקומות אחרים הוא יהיה לא טוב. הכל תלוי בקונטקסט.

אני אמחיש עם דוגמה נוספת. ערכתי קוד בג׳אווהסקריפט וביקשתי באמצעות GitHub copilot להוסיף לאובייקט שנכנס לתוך ה-localStorage גם פרמטר בשם tkn. זו הקוד שהוא הציע:

// Example object with username, token, and id
const userObject = {
  username: 'userExample',
  tkn: 'someSecureToken', // Token for authentication
  id: 123 // User's unique identifier
};

// Function to store the user object in localStorage
function storeUserObject(data) {
  localStorage.setItem('userDetails', JSON.stringify(data));
}

// Function to retrieve the user object from localStorage
function getUserObject() {
  const storedData = localStorage.getItem('userDetails');
  if (storedData) {
    return JSON.parse(storedData);
  }
  return null; // Or any other appropriate handling for missing data
}

// Storing the user object
storeUserObject(userObject);

// Retrieving the user object
const retrievedUserObject = getUserObject();
console.log(retrievedUserObject);

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

הבנה של הסביבה שהקוד פועל בה והקונטקסט הם קריטיים

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

פינת ה״אני יכול לפתור את זה!״

עם מספיק RAG, קונטקסט או prompt engineering אפשר לפתור את הכל. הבינה המלאכותית תייצר לי קוד שמתאים והולם ברוב המקרים אם אני אספק לה את כל המידע. אם אני אבקש ממנה לייצר לי סכמת מסד נתונים עם נורמליזציה, היא תעשה את זה במקום טבלת הענק המקורית. אם אני אומר לה שפונקצית התמצות משרתת סיסמה של משתמשים במערכת מאובטחת מאד שלא נדרשת ל-scale, היא תיתן לי פתרון טוב – אבל אני צריך לדעת במה להאכיל אותה ובמה לא. אני צריך לדעת מה הקונטקסט.

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

התפקיד החדש של המתכנת (?)

בעבר, כשחמי היה מתכנת, אי אז בשנות השמונים העליזות, חלק ניכר מעבודתו היה לחשוב על שימושי זכרון ולכתוב בעצמו את החלקים שניהלו אותו. הוא זה שמימש את ה-Garbage collector בתוכנה שלו ובדק מתי אפשר לשחרר זכרון ואיך. בעבר המרות של מידע – למשל Big Endians\Little Endians היו חלק מהעבודה וגורמים לבאגים (!!!) הדברים האלו, שהיו חלק משמעותי מעבודת המתכנת – הם בבחינת זכרון רחוק היום. יש סיכוי שלכתוב קוד ממשי שעושה דברים – בטח ובטח דברים אטומיים – יהיו זכרון רחוק בעתיד. כבר עכשיו העבודה שלי כמתכנת השתנתה – במקום לכתוב קוד בפועל – אני מייצר קוד עם הבינה המלאכותית (שגם זו עבודה אם צריך להוסיף RAG או לעבוד עם context) ובודק אותו בשבע עיניים כדי לראות אם הוא מתאים וכמובן מאד מאד מקפיד על תהליכי CI כדי שלא ייכנסו לי כל מיני שנינגנס ומרעין בישין לקוד.

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

פוסטים נוספים שכדאי לקרוא

גלילה לראש העמוד