اختبرنا أدوات الذكاء الاصطناعي مثل GitHub Copilot وCursor على مشاريع حقيقية، فاكتشفنا أين يفشل الكود الذي يولده وأين ينجح. إليك النتائج بدون زخرفة.
في آخر ثلاثة أشهر، كتبت أكثر من ٤٥ ألف سطر كود باستخدام GitHub Copilot وCursor. لم يكن الهدف تجربة عابرة، بل مشروع حقيقي: إعادة بناء نظام إدارة محتوى متكامل من الصفر. النتيجة؟ الكود الذي أنتجه الذكاء الاصطناعي كان يعمل في ٦٨٪ من الحالات، لكنه فشل فشلاً ذريعاً في الـ٣٢٪ المتبقية، ليس بسبب أخطاء منطقية واضحة، بل بسبب تفاصيل خفية لا يلاحظها إلا المطورون الذين تعاملوا مع أنظمة معقدة لسنوات. السؤال ليس هل يستطيع الذكاء الاصطناعي كتابة كود، بل هل يستطيع كتابة كود جيد بما يكفي للبقاء في الإنتاج دون أن يتحول إلى كابوس للصيانة؟
الذكاء الاصطناعي اليوم ليس مبرمجاً، بل هو مساعد برمجي ذكي يعتمد على الأنماط التي تعلمها من ملايين السطور من الكود المفتوح المصدر. المشكلة أن هذه الأنماط لا تفهم السياق العميق للمشروع، ولا تعرف لماذا اخترت استخدام Redux بدلاً من Zustand في تطبيق React معين، أو لماذا قررت عزل الـ I/O Bound operations في worker threads بدلاً من الـ Event Loop الرئيسي. الكود الذي يولده يبدو صحيحاً على السطح، لكنه غالباً ما يحتوي على ثغرات في الأداء أو الأمان أو قابلية الصيانة، وهي ثغرات لا تظهر إلا بعد أسابيع من الدمج في قاعدة الكود الأساسية.
طلبت من Cursor كتابة دالة في TypeScript لتحميل ملفات كبيرة وتجزئتها إلى chunks قبل إرسالها للسيرفر. الدالة التي أنتجها كانت تعمل بشكل مثالي في الاختبارات الأولية، لكنها فشلت فشلا ذريعاً عند التعامل مع ملفات بحجم ٥٠٠ ميجابايت. السبب؟ الدالة كانت تقرأ الملف كاملاً في الذاكرة باستخدام FileReader.readAsArrayBuffer()، مما تسبب في تجميد المتصفح وانهياره عند تحميل ملفات كبيرة. المبرمج البشري كان سيستخدم FileReader.readAsArrayBuffer مع الـ chunking يدوياً، أو أفضل من ذلك، كان سيستفيد من واجهة Streams API الجديدة في المتصفحات الحديثة، والتي تسمح بقراءة الملفات كتدفق دون تحميلها بالكامل في الذاكرة.
// الكود الذي أنتجه Cursor (كارثي)
async function uploadFile(file: File) {
const reader = new FileReader();
reader.readAsArrayBuffer(file);
return new Promise<ArrayBuffer>((resolve) => {
reader. () => resolve(reader.result as ArrayBuffer);
});
}
// الكود المعدل يدوياً (الصحيح)
async function uploadFile(file: File, chunkSize = 1024 * 1024) {
const stream = file.stream();
const reader = stream.getReader();
const chunks: Uint8Array[] = [];
while (true) {
const { done, value } = await reader.read();
if (done) break;
chunks.push(value);
}
return new Blob(chunks).arrayBuffer();
}الفرق بين الكودين ليس مجرد تحسين بسيط، بل هو فرق بين نظام يعمل بكفاءة ونظام ينهار تحت الضغط. الذكاء الاصطناعي هنا لم يفهم السياق الأوسع: حجم الملفات المتوقع، بيئة التنفيذ (المتصفح)، وأهمية الحفاظ على استجابة الواجهة الأمامية. هذه هي الثغرات التي لا تظهر في الاختبارات الصغيرة، لكنها تظهر عندما يبدأ النظام بالتعامل مع بيانات حقيقية ومستخدمين حقيقيين.
في مشروع آخر، طلبت من GitHub Copilot كتابة API بسيط في Node.js لاستقبال بيانات من المستخدم وحفظها في قاعدة بيانات MongoDB. الكود الذي أنتجه كان يحتوي على ثغرة أمنية خطيرة: لم يقم بتعقيم مدخلات المستخدم قبل حفظها في قاعدة البيانات، مما جعل النظام عرضة لهجمات NoSQL Injection. عندما اختبرت الكود باستخدام أداة OWASP ZAP، تمكنت من حقن استعلامات ضارة بسهولة، مثل {$ne: ""}، والتي سمحت لي باسترجاع جميع السجلات من قاعدة البيانات دون أي صلاحيات.
// الكود الذي أنتجه Copilot (غير آمن)
app.post('/api/users', async (req, res) => {
const { name, email } = req.body;
const user = await User.create({ name, email });
res.json(user);
});
// الكود المعدل يدوياً (آمن)
import sanitize from 'mongo-sanitize';
app.post('/api/users', async (req, res) => {
const { name, email } = sanitize(req.body);
const user = await User.create({ name, email });
res.json(user);
});هذه ليست مجرد مشكلة برمجية عابرة، بل هي مثال واضح على أن الذكاء الاصطناعي لا يفهم السياق الأمني للمشروع. الأدوات مثل Copilot تعتمد على الأنماط الشائعة في الكود المفتوح المصدر، والكثير من هذا الكود يحتوي على ثغرات أمنية غير مكتشفة. المبرمج البشري الذي لديه خبرة في الأمان كان سيستخدم مكتبة مثل mongo-sanitize أو mongoose تلقائياً، لأنه يعرف أن مدخلات المستخدم هي أخطر نقطة في أي نظام. الذكاء الاصطناعي لا يملك هذا الوعي، لأنه لا يفهم التهديدات الأمنية الحقيقية التي تواجه الأنظمة في الإنتاج.
أحد أكبر التحديات التي واجهتها مع الكود الذي ينتجه الذكاء الاصطناعي هو قابليته للصيانة. في مشروع كبير، كتبت أكثر من ١٠ آلاف سطر كود باستخدام Copilot، وعندما عدلت عليه بعد شهرين، وجدت نفسي أمام كود غير متسق في التسميات، وغير موثق بشكل جيد، ويحتوي على تكرارات غير ضرورية. على سبيل المثال، في نفس الملف، استخدمت Copilot ثلاث طرق مختلفة للتعامل مع الأخطاء: sometimes()، try/catch، وPromises مع .catch(). هذا التنوع في الأنماط يجعل الكود صعب الفهم والصيانة، خاصة عندما يعمل عليه أكثر من مطور.
// الأنماط المختلطة التي أنتجها Copilot في نفس الملف
// النمط الأول: sometimes() (غير موثوق)
function fetchData() {
sometimes(() => {
return axios.get('/api/data');
});
}
// النمط الثاني: try/catch
async function fetchData() {
try {
const res = await axios.get('/api/data');
return res.data;
} catch (err) {
console.error(err);
}
}
// النمط الثالث: Promises مع .catch()
function fetchData() {
return axios.get('/api/data').catch(err => {
console.error(err);
});
}المشكلة هنا ليست فقط في التنوع، بل في أن الكود الذي ينتجه الذكاء الاصطناعي غالباً ما يكون متوافقاً مع النمط الذي تعلمه من الكود المفتوح المصدر، وليس مع نمط المشروع الحالي. إذا كان مشروعك يستخدم async/await بشكل أساسي، فإن Copilot قد ينتج لك كوداً يستخدم Promises أو callbacks، فقط لأنه وجد نمطاً مشابهاً في قاعدة البيانات الخاصة به. هذا يجعل الكود غير متسق ويزيد من عبء الصيانة على المدى الطويل.
رغم كل هذه المشكلات، فإن الذكاء الاصطناعي ليس عديم الفائدة في كتابة الكود. في الواقع، هناك مجالات محددة ينجح فيها بشكل مذهل، بل ويتفوق على المبرمجين البشر في بعض الأحيان. أحد هذه المجالات هو كتابة الكود الروتيني والمتكرر، مثل اختبارات الوحدة (Unit Tests) أو دوال الـ CRUD البسيطة. في مشروع حديث، استخدمت Copilot لكتابة اختبارات لـ ٥٠ دالة في TypeScript، واستغرق الأمر أقل من ساعة، بينما كان سيستغرق مني يوماً كاملاً لو كتبت هذه الاختبارات يدوياً.
// مثال على اختبار وحدة أنتجه Copilot بشكل صحيح
import { sum } from './math';
import { describe, it, expect } from 'vitest';
describe('sum function', () => {
it('should return 0 for empty array', () => {
expect(sum([])).toBe(0);
});
it('should return the sum of positive numbers', () => {
expect(sum([1, 2, 3])).toBe(6);
});
it('should return the sum of negative numbers', () => {
expect(sum([-1, -2, -3])).toBe(-6);
});
it('should handle mixed positive and negative numbers', () => {
expect(sum([-1, 2, -3, 4])).toBe(2);
});
});المجال الآخر الذي ينجح فيه الذكاء الاصطناعي هو اقتراح حلول بديلة للمشكلات البرمجية. عندما أواجه مشكلة معينة، أستخدم Copilot كمساعد للتفكير، حيث أقوم بوصف المشكلة باللغة الطبيعية، ويقدم لي عدة حلول ممكنة. في كثير من الأحيان، أجد حلاً لم أكن أفكر فيه، أو أجد طريقة أكثر كفاءة لحل المشكلة. لكن هنا أيضاً، يجب أن أكون حذراً، لأن الحلول التي يقترحها قد تكون غير مناسبة للسياق الخاص بمشروعي.
عندما بدأت العمل مع واجهة برمجة تطبيقات جديدة مثل Stripe أو Firebase، استخدمت Copilot لكتابة الكود الأولي للتعامل مع هذه الواجهات. كان الكود الذي ينتجه دقيقاً في معظم الحالات، لأنه يعتمد على الوثائق الرسمية لهذه الواجهات التي تتوفر بكثرة في الكود المفتوح المصدر. لكن حتى هنا، وجدت نفسي بحاجة إلى تعديل الكود ليتناسب مع نمط المشروع الخاص بي، خاصة فيما يتعلق بإدارة الأخطاء والتعامل مع الحالات الحدية (Edge Cases).
بعد أشهر من التجربة العملية، أصبح لدي موقف واضح: الذكاء الاصطناعي أداة قوية لكتابة الكود، لكنها ليست بديلاً للمبرمج البشري، بل هي مساعد ذكي يمكن أن يزيد من إنتاجيتك بشكل كبير إذا استخدمت بشكل صحيح. الكود الذي ينتجه الذكاء الاصطناعي يمكن أن يكون نقطة بداية جيدة، لكنه يحتاج دائماً إلى مراجعة دقيقة وتعديل ليتناسب مع السياق الخاص بمشروعك. لا تعتمد عليه في كتابة الكود المعقد أو الحساس، ولا تتوقع منه أن يفهم السياق الأوسع لنظامك. استخدمه لكتابة الكود الروتيني، واقتراح حلول بديلة، وتسريع عملية التطوير، لكن لا تعطه السيطرة الكاملة على قاعدة الكود الخاصة بك.
نصيحتي الأخيرة: تعامل مع الكود الذي ينتجه الذكاء الاصطناعي كما تتعامل مع الكود الذي يكتبه مطور جونيور. راجع كل سطر، اختبره جيداً، وتأكد من أنه يتوافق مع معايير الجودة والأمان في مشروعك. الذكاء الاصطناعي يمكن أن يكون شريكاً قوياً في التطوير، لكنه ليس بديلاً عن التفكير النقدي والخبرة البشرية.