بعد عامين من استخدام GitHub Copilot وCursor وClaude لكتابة الكود، اكتشفت أن الذكاء الاصطناعي جيد في كتابة الأكواد البسيطة، لكنه يفشل في المشاريع المعقدة. إليك ما يحدث خلف الكواليس وكيف تتعامل مع هذه الأدوات بذكاء.
في آخر مشروع عملت عليه، استخدمت Cursor لكتابة خدمة كاملة للتعامل مع الـ WebSockets. الكود الأولي خرج في 15 دقيقة، جميل ومرتب، لكنه فشل في أول اختبار تحميل حقيقي. المشكلة؟ الذكاء الاصطناعي كتب كوداً متزامناً للتعامل مع الـ I/O Bound Tasks، ما تسبب في تجميد السيرفر تحت ضغط 500 اتصال متزامن. هذه ليست حادثة فردية، بل نمط يتكرر في كل مشروع أعمل عليه اليوم. الذكاء الاصطناعي أصبح جزءاً لا يتجزأ من سير عمل المطورين، لكن هل هو جيد بما يكفي ليحل محلنا فعلاً؟
في هذا المقال، سأفكك ما يحدث عندما يطلب منك الذكاء الاصطناعي كتابة دالة أو خدمة كاملة. سأريك أين يتفوق، وأين يفشل فشلاً ذريعاً، وكيف يمكنك استغلال هذه الأدوات دون أن تقع في الفخاخ التي يقع فيها معظم المطورين اليوم. سأستخدم أمثلة حقيقية من مشاريع مفتوحة المصدر ومشروعات عملت عليها شخصياً، مع أكواد كاملة قابلة للتنفيذ.
عندما تطلب من GitHub Copilot أو Claude كتابة دالة، لا يحدث سحر. خلف الكواليس، هناك نموذج لغة كبير (LLM) مدرب على مليارات الأسطر من الكود المفتوح المصدر. هذا النموذج لا يفهم الكود بالمعنى الحقيقي، بل يتنبأ بالنص التالي بناءً على السياق الذي قدمته له. مثلاً، إذا كتبت تعليقاً يقول "// دالة لحساب المتوسط الحسابي"، فالنموذج سيبحث في ذاكرته عن الأنماط الشائعة لكتابة هذه الدالة في مشاريع مشابهة.
المشكلة هنا أن هذه النماذج لا تفهم السياق الأوسع للمشروع. هي تعرف أن دالة لحساب المتوسط يجب أن تأخذ مصفوفة وتعيد رقماً، لكنها لا تعرف إذا كان المشروع يستخدم مكتبة معينة للتعامل مع الأرقام الكبيرة، أو إذا كان هناك متطلب خاص للأداء. لذلك، غالباً ما تخرج الدوال مكتوبة بأسلوب عام جداً، وأحياناً تستخدم مكتبات غير متوافقة مع باقي المشروع. مثلاً، في مشروع Node.js كنت أعمل عليه، اقترح Copilot استخدام مكتبة Bluebird للـ Promises، بينما المشروع بأكمله يستخدم async/await الأصلي.
// مثال على كود اقترحه Copilot لمشروع يستخدم async/await أصلي
// لكن الكود يستخدم Bluebird بدون داعٍ
const Promise = require('bluebird');
async function calculateAverage(numbers) {
if (!numbers.length) return 0;
const sum = await Promise.reduce(numbers, (acc, num) => acc + num, 0);
return sum / numbers.length;
}
// الكود الصحيح بدون Bluebird
async function calculateAverageCorrect(numbers) {
if (!numbers.length) return 0;
const sum = numbers.reduce((acc, num) => acc + num, 0);
return sum / numbers.length;
}رغم كل العيوب، هناك مجالات محددة يتفوق فيها الذكاء الاصطناعي بشكل واضح. أولاً، كتابة الأكواد البسيطة والمتكررة مثل دوال الـ CRUD في الـ Backend. مثلاً، كتابة دالة لحفظ مستخدم في قاعدة بيانات MongoDB أو PostgreSQL. هذه الدوال غالباً ما تكون متشابهة جداً بين المشاريع، والذكاء الاصطناعي يكتبها بسرعة ودقة عالية. في مشروع حديث، استخدمت Claude لكتابة 12 دالة CRUD لاثنتي عشرة مجموعة في قاعدة البيانات، واستغرق الأمر 20 دقيقة فقط بدلاً من ساعتين لو كتبتها يدوياً.
ثانياً، كتابة الاختبارات الآلية. الذكاء الاصطناعي جيد جداً في كتابة اختبارات الوحدة (Unit Tests) والاختبارات التكاملية (Integration Tests) إذا قدمت له الكود الأساسي. مثلاً، إذا كتبت دالة تقوم بتصفية مصفوفة من الأرقام، فالذكاء الاصطناعي يستطيع كتابة اختبارات تغطي الحالات العادية وحالات الحافة مثل المصفوفة الفارغة أو المصفوفة التي تحتوي على قيم غير صحيحة. في أحد المشاريع، استخدمت Cursor لكتابة اختبارات لـ API مكتوب بـ Express، وغطى 90% من الحالات التي كنت سأكتبها يدوياً.
// دالة بسيطة لتصفية الأرقام الزوجية
function filterEvenNumbers(numbers: number[]): number[] {
return numbers.filter(num => num % 2 === 0);
}
// اختبارات اقترحها Cursor
import { expect } from 'chai';
import { filterEvenNumbers } from './utils';
describe('filterEvenNumbers', () => {
it('should return empty array for empty input', () => {
expect(filterEvenNumbers([])).to.deep.equal([]);
});
it('should return only even numbers', () => {
expect(filterEvenNumbers([1, 2, 3, 4, 5, 6])).to.deep.equal([2, 4, 6]);
});
it('should handle negative numbers', () => {
expect(filterEvenNumbers([-2, -1, 0, 1, 2])).to.deep.equal([-2, 0, 2]);
});
it('should handle non-integer numbers', () => {
expect(filterEvenNumbers([1.5, 2, 3.2, 4])).to.deep.equal([2, 4]);
});
});الذكاء الاصطناعي يفشل في ثلاث حالات رئيسية: التعامل مع الأكواد المعقدة، فهم متطلبات المشروع الأوسع، والتعامل مع المشاكل غير الواضحة. أولاً، الأكواد المعقدة مثل الخوارزميات المتقدمة أو الأنظمة الموزعة. مثلاً، عندما طلبت من Claude كتابة خوارزمية لتوزيع الـ Load على عدة سيرفرات باستخدام الـ Consistent Hashing، خرج الكود مليئاً بالأخطاء المنطقية. المشكلة أن هذه الخوارزميات تتطلب فهماً عميقاً لكيفية توزيع البيانات، والذكاء الاصطناعي لا يملك هذا الفهم، بل ينسخ الأنماط الشائعة فقط.
ثانياً، فهم متطلبات المشروع الأوسع. مثلاً، في مشروع كنت أعمل عليه، استخدمنا مكتبة مخصصة للتعامل مع الـ Authentication، وكان هناك متطلب أمني يمنع تخزين الـ Tokens في الـ Local Storage. عندما طلبت من Copilot كتابة دالة لتسجيل الدخول، اقترح تخزين الـ Token في الـ Local Storage، وهو ما يتعارض مع متطلبات الأمان في المشروع. هذا النوع من الأخطاء يصعب اكتشافه لأنه يبدو صحيحاً من الناحية التقنية، لكنه خاطئ من الناحية العملية.
// مثال على كود خاطئ اقترحه Copilot
function loginUser(email, password) {
return fetch('/api/login', {
method: 'POST',
body: JSON.stringify({ email, password }),
headers: { 'Content-Type': 'application/json' }
})
.then(res => res.json())
.then(data => {
// تخزين الـ Token في Local Storage (خاطئ أمنياً)
localStorage.setItem('authToken', data.token);
return data.user;
});
}
// الكود الصحيح باستخدام HttpOnly Cookies
function loginUserCorrect(email, password) {
return fetch('/api/login', {
method: 'POST',
body: JSON.stringify({ email, password }),
headers: { 'Content-Type': 'application/json' },
credentials: 'include' // يسمح بإرسال الـ Cookies
})
.then(res => res.json())
.then(data => data.user);
}أصعب الأخطاء هي تلك التي لا تظهر في الكود نفسه، بل في كيفية تفاعله مع النظام الأوسع. مثلاً، في مشروع Node.js، طلبت من الذكاء الاصطناعي كتابة دالة لقراءة ملف كبير ومعالجته. الكود الذي خرج كان صحيحاً من الناحية النحوية، لكنه استخدم الـ Synchronous File Reading، ما تسبب في تجميد الـ Event Loop تحت الضغط. المشكلة أن الذكاء الاصطناعي لا يفهم مفهوم الـ Event Loop أو كيف يعمل Node.js داخلياً، لذلك يكتب الكود كما لو كان يكتبه في لغة متزامنة مثل Python.
// كود خاطئ: قراءة ملف بشكل متزامن
function processLargeFileSync(filePath) {
const data = fs.readFileSync(filePath, 'utf8'); // تجميد الـ Event Loop
return data.split('\n').filter(line => line.trim() !== '');
}
// الكود الصحيح: قراءة ملف بشكل غير متزامن
async function processLargeFile(filePath) {
const data = await fs.promises.readFile(filePath, 'utf8');
return data.split('\n').filter(line => line.trim() !== '');
}الذكاء الاصطناعي أداة قوية، لكنها ليست بديلاً عن المطور. لاستخدامها بفعالية، يجب أن تتبع ثلاث قواعد ذهبية. أولاً، استخدم الذكاء الاصطناعي للكتابة الأولية فقط، وليس للكتابة النهائية. مثلاً، اطلب منه كتابة الهيكل الأساسي لدالة أو خدمة، ثم راجع الكود بعناية وعدله ليتناسب مع مشروعك. ثانياً، أعطِ الذكاء الاصطناعي أكبر قدر ممكن من السياق. بدلاً من كتابة "اكتب دالة لحساب المتوسط"، اكتب "اكتب دالة لحساب المتوسط الحسابي لمصفوفة من الأرقام الكبيرة باستخدام مكتبة bignumber.js في مشروع Node.js"، هذا يزيد فرص خروج الكود صحيحاً.
ثالثاً، اختبر الكود الذي ينتجه الذكاء الاصطناعي بنفس الدقة التي تختبر بها الكود الذي تكتبه يدوياً. لا تفترض أن الكود صحيح لمجرد أنه خرج من نموذج لغة كبير. في أحد المشاريع، استخدمنا Cursor لكتابة خدمة كاملة للتعامل مع المدفوعات، واكتشفنا بعد شهر من الاستخدام أن هناك مشكلة في كيفية حساب الضرائب في بعض الولايات الأمريكية. المشكلة كانت في دالة صغيرة كتبها الذكاء الاصطناعي، ولم نكتشفها إلا بعد أن اشتكى أحد العملاء.
في الأشهر الستة الماضية، استخدمت ثلاث أدوات رئيسية لكتابة الكود بالذكاء الاصطناعي: GitHub Copilot، Cursor، وClaude. كل أداة لها نقاط قوة وضعف. Copilot هو الأفضل في التكامل مع بيئة التطوير، خاصةً إذا كنت تستخدم VS Code. هو جيد جداً في إكمال الأسطر وكتابة الدوال الصغيرة، لكنه ضعيف في فهم السياق الأوسع للمشروع. مثلاً، إذا كنت تعمل على مشروع يستخدم TypeScript، فسيقترح أحياناً أكواداً مكتوبة بـ JavaScript العادي.
Cursor، من ناحية أخرى، هو الأفضل في فهم السياق الأوسع. هو يستطيع قراءة ملفات المشروع بأكملها وفهم كيفية تفاعلها معاً. مثلاً، إذا كنت تعمل على خدمة في مشروع كبير، فCursor يستطيع قراءة جميع الملفات المتعلقة بهذه الخدمة واقتراح كود يتناسب معها. المشكلة أن Cursor أبطأ قليلاً من Copilot، وأحياناً يقترح أكواداً معقدة جداً بالنسبة للحاجة البسيطة.
Claude هو الأفضل في كتابة الأكواد الطويلة والمعقدة، مثل كتابة خدمة كاملة أو خوارزمية متقدمة. هو يستطيع كتابة مئات الأسطر من الكود في جلسة واحدة، ويحافظ على التناسق في الأسلوب. المشكلة أن Claude لا يتكامل مع بيئة التطوير، لذلك عليك نسخ ولصق الكود يدوياً، وهذا قد يكون مملاً في المشاريع الكبيرة.
الذكاء الاصطناعي أداة قوية، لكنه ليس بديلاً عن المطور. استخدمه لكتابة الأكواد البسيطة والمتكررة، وراجع الكود الذي ينتجه بعناية، واختبره بنفس الدقة التي تختبر بها الكود الذي تكتبه يدوياً. لا تعتمد عليه في كتابة الأكواد المعقدة أو فهم متطلبات المشروع الأوسع. إذا اتبعت هذه القواعد، فستستفيد من الذكاء الاصطناعي دون أن تقع في الفخاخ التي يقع فيها معظم المطورين اليوم. وفي النهاية، تذكر أن الذكاء الاصطناعي لا يفهم الكود، بل ينسخ الأنماط فقط. الفهم الحقيقي يأتي منك أنت كمطور.