اختبرنا نماذج الذكاء الاصطناعي في كتابة كود حقيقي لمشاريع معقدة، النتائج كانت مفاجئة: بعضها أنقذنا من ساعات من العمل، وبعضها خلق كوارث صامتة في الإنتاج. إليك التفاصيل الدقيقة لما يستطيع فعله وما يخفيه تحت السطح.
في آخر ثلاثة أشهر، كتبت نماذج الذكاء الاصطناعي المختلفة أكثر من ٤٧ ألف سطر كود لي في مشاريع حقيقية. بعضها كان مذهلاً لدرجة أنني تركت التعليقات فيه كما هي دون تعديل، وبعضها الآخر تسبب في انهيار سيرفر الإنتاج مرتين. الحقيقة هي أن الذكاء الاصطناعي لا يكتب كوداً جيداً بشكل مطلق - بل يكتب كوداً يمكن أن يكون جيداً في سياقات محددة جداً، ومدمراً في سياقات أخرى. الفرق بين الاثنين لا يكمن في جودة النموذج نفسه، بل في كيفية استخدامه وفهم حدوده التقنية.
عندما نتحدث عن كتابة الكود بالذكاء الاصطناعي، لا نتحدث عن سحر أو معجزات. خلف كل سطر كود يولده نموذج مثل GPT-4 أو Claude، هناك عملية رياضية معقدة تعتمد على توقع الكلمة التالية بناءً على السياق. هذا يعني أن النموذج لا يفهم الكود بالمعنى الذي نفهمه نحن كمطورين - بل يتنبأ بالنمط الأكثر احتمالاً بناءً على البيانات التي تدرب عليها. هذا الفرق الأساسي يفسر لماذا يمكن للذكاء الاصطناعي أن يولد كوداً يبدو مثالياً في النظرة الأولى، لكنه ينهار عند أول اختبار حقيقي تحت ضغط الإنتاج.
كان لدي مشروع ترقية لنظام قديم مكتوب بـ PHP 5.3، مليء بالـ spaghetti code و SQL queries مدمجة مباشرة داخل ملفات الـ views. المهمة كانت ترحيل النظام إلى Laravel 10 مع الحفاظ على نفس الوظائف. بعد أسبوعين من العمل اليدوي، كنت قد انتهيت من ٣٠٪ فقط من المهمة، وكان الكود الناتج مليئاً بالـ bugs بسبب الفروقات الدقيقة بين الإصدارات. قررت تجربة GitHub Copilot في هذه الحالة.
النتائج كانت مذهلة: Copilot لم يترجم الكود فقط، بل أعاد كتابته بشكل أكثر نظافة. مثلاً، عندما كتبت تعليقاً مثل // Convert this raw SQL to Laravel query builder، كان يولد الكود التالي مباشرة:
// Original raw SQL in legacy code
$query = "SELECT * FROM users WHERE status = 'active' AND created_at > '2023-01-01' ORDER BY name";
$result = mysql_query($query);
// Copilot generated Laravel equivalent
$users = DB::table('users')
->where('status', 'active')
->where('created_at', '>', '2023-01-01')
->orderBy('name')
->get();لكن الأهم من ذلك هو كيف تعامل مع الـ business logic المعقدة. كان هناك جزء في النظام القديم يحسب الضرائب بناءً على قواعد معقدة تعتمد على الولاية ونوع المنتج وسعره. الكود الأصلي كان عبارة عن ٢٠٠ سطر من if-else متداخلة. عندما طلبت من Copilot إعادة كتابته، أنتج الكلاس التالي:
class TaxCalculator {
private $taxRules = [
'CA' => [
'standard' => 0.0725,
'food' => 0.01,
'clothing' => 0.05
],
'NY' => [
'standard' => 0.08875,
'food' => 0.0,
'clothing' => 0.04
]
];
public function calculate($state, $productType, $price) {
if (!isset($this->taxRules[$state])) {
throw new InvalidArgumentException("Unsupported state: {$state}");
}
$rate = $this->taxRules[$state][$productType] ?? $this->taxRules[$state]['standard'];
return $price * $rate;
}
}هذا الكود لم يكن مجرد ترجمة حرفية، بل إعادة تصميم كاملة للحل. لاحظ كيف استخدم مصفوفة بدلاً من الـ if-else المتداخلة، وكيف أضاف معالجة للأخطاء باستخدام الاستثناءات. في هذه الحالة، وفر لي الذكاء الاصطناعي أكثر من ١٥ ساعة من العمل اليدوي، وكان الكود الناتج أفضل بكثير من أي شيء كنت سأكتبه بنفسي تحت ضغط الوقت.
في مشروع آخر، كنا نعمل على نظام معالجة دفعات مالية كبيرة الحجم. كان لدينا script بلغة Python لمعالجة ملفات CSV تحتوي على ملايين السجلات. طلبت من GPT-4 تحسين الكود لجعله أسرع وأكثر كفاءة. الكود الأصلي كان بسيطاً لكنه بطيء:
def process_csv(file_path):
with open(file_path, 'r') as file:
for line in file:
data = line.strip().split(',')
process_record(data)
def process_record(record):
# Complex business logic here
passالكود الذي ولده GPT-4 كان يبدو رائعاً في البداية:
import pandas as pd
def process_csv_optimized(file_path):
df = pd.read_csv(file_path)
df.apply(process_record, axis=1)
def process_record(record):
# Same business logic
passفي بيئة التطوير، كان هذا الكود يعمل بشكل رائع. كان أسرع بخمس مرات من النسخة الأصلية عند معالجة ملفات صغيرة. لكن عندما قمنا بتشغيله على ملف الإنتاج الذي يحتوي على ١٢ مليون سجل، حدث الكارثة. بعد ٤ ساعات من المعالجة، توقف السيرفر عن الاستجابة. اكتشفنا لاحقاً أن pandas كان يحاول تحميل الملف بالكامل في الذاكرة، مما تسبب في استهلاك أكثر من ٣٢ جيجابايت من الـ RAM. المشكلة الحقيقية هي أن GPT-4 لم يفهم السياق الحقيقي للاستخدام - لم يعرف أن الملفات ستكون بهذا الحجم الهائل.
الحل النهائي الذي عملناه كان مزيجاً بين الأسلوبين:
def process_csv_chunked(file_path, chunk_size=10000):
for chunk in pd.read_csv(file_path, chunksize=chunk_size):
chunk.apply(process_record, axis=1)
def process_record(record):
# Same business logic with optimizations
passهذا المثال يوضح نقطة مهمة جداً: الذكاء الاصطناعي جيد في اقتراح حلول نمطية، لكنه ضعيف جداً في فهم السياق الحقيقي للمشكلة. في هذه الحالة، كان الحل الأمثل يتطلب معرفة عميقة بحدود الذاكرة المتاحة وحجم البيانات المتوقع، وهي معلومات لم تكن متوفرة للنموذج.
لفهم لماذا يكتب الذكاء الاصطناعي كوداً جيداً أحياناً وسيئاً أحياناً أخرى، يجب أن نفهم كيف يعمل فعلاً. عندما تطلب من نموذج مثل GPT-4 كتابة دالة بلغة Python، فهو لا يفهم البرمجة بالمعنى التقليدي. بدلاً من ذلك، يقوم بعملية تسمى next token prediction - أي أنه يتنبأ بالكلمة التالية (أو الرمز البرمجي التالي) بناءً على السياق الذي قدمته له والبيانات التي تدرب عليها.
هذه العملية تعتمد على ما يسمى بالـ attention mechanism في النماذج اللغوية الكبيرة. عندما تطلب كتابة دالة لحساب مضروب العدد، يقوم النموذج بتحليل السياق الذي قدمته (مثلاً: "اكتب دالة بلغة Python لحساب مضروب العدد باستخدام التكرار") ويقارن هذا السياق مع ملايين الأمثلة المشابهة التي تدرب عليها. ثم يولد الكود سطراً بسطر، مع مراعاة السياق الذي ولده سابقاً.
المشكلة تكمن في أن هذا النهج لا يفهم المعنى الحقيقي للكود. مثلاً، يمكن للنموذج أن يولد الكود التالي بسهولة:
def factorial(n):
if n == 0:
return 1
else:
return n * factorial(n - 1)هذا الكود صحيح من الناحية الرياضية، لكنه قد يسبب stack overflow إذا تم استدعاؤه بقيمة كبيرة لـ n. النموذج لا يفهم مفهوم الـ call stack أو حدود الذاكرة، بل فقط يتنبأ بالنمط الأكثر شيوعاً في بيانات التدريب. هذا هو السبب في أن الكود الذي يولده الذكاء الاصطناعي غالباً ما يكون صحيحاً نحوياً، لكنه قد يكون غير مناسب للسياق الحقيقي للاستخدام.
أحد أكبر المشاكل في توليد الكود بالذكاء الاصطناعي هو ما يسمى بالـ hallucination - أي توليد كود يبدو صحيحاً لكنه في الواقع غير موجود أو غير صالح. مثلاً، في إحدى المرات طلبت من نموذج توليد كود لاستخدام مكتبة معينة، فولد الكود التالي:
from fastapi import FastAPI
from fastapi.security import OAuth2PasswordBearer
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/items/")
async def read_items(token: str = Depends(oauth2_scheme)):
return {"token": token}الكود يبدو صحيحاً تماماً، لكنه يحتوي على مشكلة: مكتبة FastAPI لا تحتوي على OAuth2PasswordBearer في المسار fastapi.security. هذه الكلاس موجودة في fastapi.security.oauth2. هذا النوع من الأخطاء شائع جداً، خاصة مع المكتبات التي تتغير بسرعة. النموذج يتنبأ بالنمط الأكثر احتمالاً بناءً على البيانات التي تدرب عليها، لكنه لا يفهم البنية الفعلية للمكتبة في الوقت الحالي.
هناك قيد تقني آخر مهم جداً: حجم الـ context window. معظم النماذج الحالية لها حد أقصى للسياق الذي يمكنها معالجته في المرة الواحدة. مثلاً، GPT-4 لديه context window بحجم ٣٢ ألف token تقريباً. هذا يعني أنك إذا طلبت منه كتابة كود معقد جداً أو تحليل ملف كبير، قد يفقد السياق في منتصف العملية.
في إحدى التجارب، طلبت من نموذج تحليل مشروع Node.js كامل يحتوي على ٥٠ ملفاً. بعد الملف العشرين تقريباً، بدأ النموذج يولد كوداً غير متسق مع الملفات السابقة. مثلاً، كان يولد دوال تستخدم متغيرات غير معرفة أو يستدعي كلاسات غير موجودة. هذا يحدث لأن النموذج ينسى السياق الأولي عندما يصل إلى حد الـ context window الخاص به.
بعد كل هذه التجارب، أستطيع القول بثقة أن الذكاء الاصطناعي يكون مفيداً جداً في كتابة الكود في الحالات التالية:
في تجربتي، أفضل استخدام للذكاء الاصطناعي في كتابة الكود هو كمساعد ذكي وليس كمبرمج مستقل. مثلاً، في مشروع حديث، استخدمت Claude لكتابة الـ unit tests لمشروع TypeScript معقد. طلبت منه كتابة اختبارات لـ ٢٠ دالة مختلفة، وكان الناتج مذهلاً:
describe('calculateDiscount', () => {
it('should return 0 for non-premium users with amount < 100', () => {
expect(calculateDiscount(50, false)).toBe(0);
});
it('should return 10% discount for premium users with amount < 100', () => {
expect(calculateDiscount(50, true)).toBe(5);
});
it('should return 15% discount for non-premium users with amount >= 100', () => {
expect(calculateDiscount(150, false)).toBe(22.5);
});
it('should return 20% discount for premium users with amount >= 100', () => {
expect(calculateDiscount(150, true)).toBe(30);
});
it('should throw error for negative amount', () => {
expect(() => calculateDiscount(-50, false)).toThrow('Amount cannot be negative');
});
});هذه الاختبارات غطت حالات كثيرة لم أكن لأفكر فيها بنفسي، خاصة الحالات الحدية مثل القيم السالبة. لكن الأهم هو أنني راجعت كل اختبار بعناية للتأكد من أنه يختبر السلوك الصحيح بالفعل. الذكاء الاصطناعي هنا وفر لي الوقت في كتابة الـ boilerplate الخاص بالاختبارات، لكنه لم يحل محل فهمي العميق للكود الذي أختبره.
هناك خطر حقيقي في الاعتماد المفرط على الذكاء الاصطناعي في كتابة الكود: وهم الكفاءة. الكود الذي يولده الذكاء الاصطناعي غالباً ما يبدو مثالياً في النظرة الأولى، لكنه قد يحتوي على مشاكل عميقة لا تظهر إلا عند الفحص الدقيق أو في ظروف الإنتاج الحقيقية.
في إحدى الشركات التي عملت معها، قرر فريق تطوير استخدام GitHub Copilot لكتابة كامل مشروع جديد. بعد ثلاثة أشهر، اكتشفوا أن المشروع مليء بالـ memory leaks و race conditions. المشكلة لم تكن في الكود نفسه، بل في الطريقة التي تم بها استخدام الأداة. مثلاً، كان Copilot يولد دوال async بدون التعامل الصحيح مع الأخطاء، مما يسبب تسريبات في الـ event loop:
// Bad code generated by Copilot
async function fetchData() {
const resp await fetch('https://api.example.com/data');
const data = await response.json();
return data;
}
// What it should be
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error('Error fetching data:', error);
throw error;
}
}المشكلة الأخرى هي أن الكود الذي يولده الذكاء الاصطناعي غالباً ما يكون غير متناسق. مثلاً، قد يستخدم أنماط برمجة مختلفة لنفس الوظيفة في أماكن مختلفة من المشروع. هذا يجعل الكود صعب الصيانة على المدى الطويل. في أحد المشاريع، وجدنا أن Copilot استخدم ثلاثة أنماط مختلفة للتعامل مع التواريخ في نفس الملف:
// Pattern 1: Using Date object directly
const date1 = new Date('2023-01-01');
// Pattern 2: Using moment.js
const date2 = moment('2023-01-01');
// Pattern 3: Using date-fns
const date3 = parseISO('2023-01-01');هذا التنوع في الأنماط يجعل الكود صعب الفهم والصيانة، خاصة للمطورين الجدد في الفريق. المشكلة الحقيقية هي أن الذكاء الاصطناعي لا يفهم مفهوم الـ code consistency - فهو يولد الكود بناءً على السياق الفوري فقط، دون مراعاة الأنماط المستخدمة في بقية المشروع.
هناك خطر آخر أكثر خطورة: الثغرات الأمنية. في دراسة حديثة أجرتها جامعة ستانفورد، وجدوا أن المطورين الذين يستخدمون أدوات الذكاء الاصطناعي لكتابة الكود كانوا أكثر عرضة لإدخال ثغرات أمنية في الكود بنسبة ٥٠٪ مقارنة بالمطورين الذين يكتبون الكود يدوياً. السبب هو أن النماذج اللغوية غالباً ما تولد كوداً يبدو صحيحاً لكنه يحتوي على ممارسات غير آمنة.
مثال شائع هو التعامل مع الـ SQL injection. طلبت من نموذج توليد كود لاستعلام قاعدة بيانات، فولد الكود التالي:
import sqlite3
def get_user(username):
c sqlite3.connect('users.db')
cursor = conn.cursor()
query = f"SELECT * FROM users WHERE username = '{username}'"
cursor.execute(query)
return cursor.fetchone()هذا الكود يبدو صحيحاً للوهلة الأولى، لكنه يحتوي على ثغرة أمنية خطيرة تسمح بـ SQL injection. النموذج لم يفهم السياق الأمني للمشكلة، بل فقط توقع النمط الأكثر شيوعاً في بيانات التدريب. الحل الصحيح يتطلب استخدام الـ parameterized queries:
def get_user(username):
c sqlite3.connect('users.db')
cursor = conn.cursor()
query = "SELECT * FROM users WHERE username = ?"
cursor.execute(query, (username,))
return cursor.fetchone()هذه الثغرات الأمنية هي الأخطر لأنها غالباً ما تمر دون اكتشاف في المراجعات الأولية للكود. المطور الذي يعتمد على الذكاء الاصطناعي قد لا يدرك وجود المشكلة أصلاً، خاصة إذا كان مبتدئاً في المجال.
بعد كل هذه التجارب، أستطيع أن أقول إن مفتاح استخدام الذكاء الاصطناعي بفعالية في كتابة الكود هو معاملته كأداة مساعدة ذكية، وليس كمبرمج مستقل. إليك بعض النصائح العملية التي أستخدمها يومياً:
أفضل طريقة لاستخدام الذكاء الاصطناعي في البرمجة هي ما أسميه "البرمجة التفاعلية". بدلاً من طلب كتابة كود كامل مرة واحدة، استخدمه في جلسات تفاعلية. مثلاً، ابدأ بطلب كتابة هيكل أساسي، ثم اطلب تحسينات محددة بناءً على النتائج الأولية. هذه الطريقة تسمح لك بتوجيه النموذج بشكل أفضل والحصول على نتائج أكثر دقة.
في النهاية، الذكاء الاصطناعي هو مجرد أداة - أداة قوية جداً، لكنها ليست سحرية. قدرتها على كتابة كود جيد تعتمد بشكل كامل على كيفية استخدامها. المطورون الذين يفهمون حدودها ويستطيعون توجيهها بشكل فعال هم الذين سيستفيدون منها حقاً، بينما الذين يعتمدون عليها بشكل أعمى سيجدون أنفسهم في مشاكل كبيرة.
الذكاء الاصطناعي لا يكتب كوداً جيداً بشكل مطلق - بل يكتب كوداً يمكن أن يكون جيداً في أيدي مهندس يفهم ما يفعله. الفرق بين الكود الجيد والسيئ في هذه الحالة ليس في النموذج نفسه، بل في المهندس الذي يستخدمه. أفضل استخدام للذكاء الاصطناعي في البرمجة اليوم هو كمساعد ذكي يساعدك على التفكير بشكل أفضل وأسرع، وليس كبديل عن التفكير أصلاً.
إذا أردت نصيحة واحدة فقط من هذا المقال، فهي هذه: استخدم الذكاء الاصطناعي لكتابة الـ ٨٠٪ المملة من الكود، لكن لا تتنازل أبداً عن الـ ٢٠٪ الصعبة التي تتطلب فهمك العميق للمشكلة والسياق. هذه الـ ٢٠٪ هي التي تفرق بين الكود الذي يعمل والكود الذي يعمل بشكل جيد تحت أي ظرف.