بعد عامين من استخدام GitHub Copilot وCursor وClaude لكتابة كود الإنتاج، هذه هي الحقيقة الصادمة: الذكاء الاصطناعي يكتب كوداً سريعاً لكنه فاشل في الكود الجيد. إليك تجارب حقيقية من مشاريع حقيقية تكشف أين يفشل وأين ينجح.
في آخر مشروع لي مع فريق مكون من 8 مطورين، قررنا تجربة شيء مختلف: استخدام Claude 3.5 Sonnet لكتابة كامل الـ backend للنظام الجديد. الفكرة كانت جذابة: بدلاً من قضاء أسبوعين في كتابة الـ API الأساسي، سنحصل على نسخة أولية خلال يومين. النتيجة؟ حصلنا على كود يعمل فعلاً، لكنه كان كابوساً هندسياً. الـ endpoints كانت تعمل، لكن الـ memory footprint كان أكبر بثلاث مرات من المعتاد، والـ event loop كان يتجمد كل 15 دقيقة بسبب دوال غير متزامنة مكتوبة بطريقة غريبة. والأسوأ؟ كان الكود مليئاً بـ hardcoded values مخفية في أماكن لا تخطر على بال. بعد أسبوع من الـ debugging، عدنا للكتابة اليدوية. هذه ليست قصة فريدة - إنها القاعدة.
الذكاء الاصطناعي اليوم قادر على كتابة كود يعمل، لكنه فاشل في كتابة كود جيد. الفرق بين الاثنين هو ما يفصل بين مشروع ناجح وآخر فاشل. الكود الجيد لا يعني مجرد أن البرنامج يعمل، بل يعني أن يكون قابلاً للصيانة، فعالاً في استخدام الموارد، وآمناً. وهنا تكمن المشكلة: نماذج اللغة الكبيرة (LLMs) لا تفهم هذه المفاهيم لأنها ببساطة لا تملك سياقاً هندسياً حقيقياً. إنها تتنبأ بالنص التالي بناءً على أنماط تعلمتها، وليس بناءً على فهم عميق للمشكلة أو النظام ككل.
طلبت من Claude كتابة نظام إدارة مهام بسيط باستخدام Node.js وExpress. أعطيتها المتطلبات التالية: "أنشئ API لإدارة المهام مع دعم للمجموعات، والمستخدمين، والأولويات. استخدم MongoDB وقم بتطبيق الـ authentication باستخدام JWT. يجب أن يكون الكود جاهزاً للإنتاج." النتيجة الأولية كانت مذهلة - حصلت على أكثر من 500 سطر من الكود خلال دقيقتين. لكن عند الفحص الدقيق، ظهرت المشاكل الحقيقية:
هذه ليست مشاكل بسيطة يمكن إصلاحها بسهولة. إنها مشاكل هيكلية تظهر أن النموذج لا يفهم المفاهيم الأساسية لهندسة البرمجيات. عندما سألت Claude عن سبب عدم استخدامه للـ indexes، كانت إجابته: "لم أعتبر أن الأداء سيكون مشكلة في هذا الحجم من البيانات." هذه إجابة غير مقبولة من مطور محترف - نحن لا نكتب كوداً بناءً على افتراضات، بل بناءً على أفضل الممارسات.
// مثال على كود سيء من Claude - لاحظوا الـ memory leak في الـ event listener
app.get('/tasks', authenticateJWT, async (req, res) => {
const userId = req.user.id;
// هذا الـ event listener لن يُزال أبداً!
process.on('uncaughtException', (err) => {
console.error('Uncaught Exception:', err);
});
// لا يوجد أي index على حقل userId في قاعدة البيانات
const tasks = await Task.find({ userId });
res.json(tasks);
});
// مثال على الـ JWT الضعيف - لا يوجد أي حماية ضد هجمات إعادة الإرسال
app.post('/login', async (req, res) => {
const { email, password } = req.body;
const user = await User.findOne({ email });
if (!user || !(await bcrypt.compare(password, user.password))) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// لا يوجد أي مدة صلاحية للتوكن!
const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET);
res.json({ token });
});في الجانب الآخر، عندما يتعلق الأمر بتحسين الكود الموجود أو كتابة أجزاء صغيرة ومحددة، فإن الذكاء الاصطناعي يمكن أن يكون أداة قوية جداً. في مشروع آخر، كان لدينا ملف JavaScript ضخم يحتوي على أكثر من 1000 سطر من الـ business logic المتشابك. بدلاً من إعادة كتابته بالكامل، استخدمنا Cursor مع Claude لتحسينه تدريجياً. النتائج كانت مذهلة:
المفتاح هنا هو أن الذكاء الاصطناعي يعمل بشكل أفضل عندما يكون لديه سياق محدد ومحدود. بدلاً من طلب كتابة نظام كامل، طلبنا منه تحسين أجزاء صغيرة من الكود، مع إعطاء تعليمات واضحة عن ما نريد تحقيقه. على سبيل المثال، بدلاً من قول "حسن هذا الكود"، قلنا: "أعد كتابة هذه الدالة بحيث تستخدم الـ binary search بدلاً من الـ linear search، وأضف الـ TypeScript types، واكتب اختباراً لها."
// قبل التحسين - دالة بطيئة ومعقدة
function findTaskById(tasks: any[], id: string): any {
for (let i = 0; i < tasks.length; i++) {
if (tasks[i].id === id) {
return tasks[i];
}
}
return null;
}
// بعد التحسين بواسطة Claude - أسرع وأكثر أماناً
interface Task {
id: string;
title: string;
completed: boolean;
// ... حقول أخرى
}
function findTaskById(tasks: Task[], id: string): Task | null {
let left = 0;
let right = tasks.length - 1;
// افترض أن الـ tasks مرتبة حسب id
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (tasks[mid].id === id) {
return tasks[mid];
} else if (tasks[mid].id < id) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return null;
}
// اختبار الوحدة الذي أضافه Claude
import { expect } from 'chai';
describe('findTaskById', () => {
it('should find a task by id in a sorted array', () => {
const tasks = [
{ id: '1', title: 'Task 1' },
{ id: '2', title: 'Task 2' },
{ id: '3', title: 'Task 3' }
];
expect(findTaskById(tasks, '2')).to.deep.equal({ id: '2', title: 'Task 2' });
});
it('should return null if task not found', () => {
const tasks = [{ id: '1', title: 'Task 1' }];
expect(findTaskById(tasks, '999')).to.be.null;
});
});الفرق بين المثالين واضح. الكود الأول كان يعمل، لكنه كان بطيئاً وغير آمن من الناحية النوعية. الكود الثاني أسرع بعشرات المرات (خاصة مع المصفوفات الكبيرة)، وأكثر أماناً بفضل الـ TypeScript، وأسهل للصيانة بفضل الاختبارات. هذا هو نوع التحسين الذي يمكن أن يقدمه الذكاء الاصطناعي عندما يُوجّه بشكل صحيح.
في أحد المشاريع، كان لدينا مشكلة غريبة: الـ server كان يتجمد بشكل عشوائي كل بضع ساعات. بعد أيام من الـ debugging اليدوي، قررنا تجربة GitHub Copilot Chat. أعطيناه الـ stack trace الكامل، وسجلات الـ logs، ووصفاً للمشكلة. خلال دقائق، أشار إلى المشكلة الحقيقية: كنا نستخدم مكتبة خارجية لمعالجة الصور، وهذه المكتبة كانت تفتح ملفات مؤقتة لكنها لم تكن تغلقها أبداً. بعد بضعة ساعات، كانت الـ file descriptors تنفذ، مما يسبب تجمد النظام.
هذه التجربة أظهرت لي شيئاً مهماً: الذكاء الاصطناعي ممتاز في العثور على الـ bugs التي لها أنماط معروفة، لكنه فاشل في اكتشاف الـ bugs التي تتطلب فهماً عميقاً للنظام. على سبيل المثال، يمكنه بسهولة اكتشاف الـ memory leaks الناتجة عن عدم إغلاق الـ file descriptors، لكنه سيفشل في اكتشاف مشكلة في الـ business logic تتطلب فهماً للسياق التجاري للمشروع.
# مثال على مشكلة بسيطة يمكن للذكاء الاصطناعي اكتشافها
import tempfile
def process_image(image_path):
# هذه المكتبة تفتح ملفاً مؤقتاً لكنها لا تغلقه!
temp_file = tempfile.NamedTemporaryFile(delete=False)
# ... معالجة الصورة ...
# الملف لا يُغلق أبداً!
return processed_image
# بعد اكتشاف المشكلة بواسطة Copilot
import tempfile
def process_image(image_path):
with tempfile.NamedTemporaryFile(delete=False) as temp_file:
# ... معالجة الصورة ...
return processed_image
# الملف يُغلق تلقائياً عند الخروج من الـ contextفي المقابل، عندما حاولنا استخدام الذكاء الاصطناعي لاكتشاف مشكلة في الـ business logic (كان النظام يسمح بإنشاء حسابات بدون التحقق من البريد الإلكتروني في بعض الحالات)، فشل فشلاً ذريعاً. المشكلة كانت تتطلب فهماً للسياق التجاري ولم يكن هناك أي نمط معروف يمكن للنموذج التعرف عليه. هذا يبرز نقطة مهمة: الذكاء الاصطناعي جيد في اكتشاف الـ bugs التقنية، لكنه سيء جداً في اكتشاف الـ bugs المنطقية أو التجارية.
كتابة الاختبارات هي واحدة من أصعب المهام في تطوير البرمجيات. إنها تتطلب فهماً عميقاً للكود، والقدرة على توقع الحالات الحدية، والصبر لكتابة سيناريوهات تغطي جميع الاحتمالات. لذلك، كان من الطبيعي أن أفكر في استخدام الذكاء الاصطناعي لهذه المهمة. النتائج كانت مختلطة:
المشكلة الأكبر هي أن الذكاء الاصطناعي يكتب اختبارات "ساذجة". إنه يكتب اختبارات تمر في معظم الحالات، لكنه يفشل في اكتشاف الحالات التي قد تفشل فيها الوظيفة في الإنتاج. على سبيل المثال، طلبت منه كتابة اختبار لدالة تحسب الخصم على منتج بناءً على الكمية المشتراة. كتب اختباراً جيداً للحالات العادية، لكنه فشل في اختبار حالة شراء كمية سلبية (التي يجب أن ترجع خطأ) أو كمية أكبر من المخزون المتاح.
// الدالة الأصلية
function calculateDiscount(quantity, price) {
if (quantity <= 0) return 0;
if (quantity < 5) return price * quantity;
if (quantity < 10) return price * quantity * 0.95;
return price * quantity * 0.9;
}
// الاختبار الذي كتبه الذكاء الاصطناعي - لاحظوا الفجوات!
test('calculateDiscount', () => {
expect(calculateDiscount(1, 100)).toBe(100);
expect(calculateDiscount(5, 100)).toBe(475);
expect(calculateDiscount(10, 100)).toBe(900);
});
// الاختبار الذي يجب أن يكون مكتوباً - يغطي الحالات الحدية
import { calculateDiscount } from './discount';
describe('calculateDiscount', () => {
it('should return 0 for non-positive quantities', () => {
expect(calculateDiscount(0, 100)).toBe(0);
expect(calculateDiscount(-1, 100)).toBe(0);
});
it('should not apply discount for quantities less than 5', () => {
expect(calculateDiscount(1, 100)).toBe(100);
expect(calculateDiscount(4, 100)).toBe(400);
});
it('should apply 5% discount for quantities between 5 and 9', () => {
expect(calculateDiscount(5, 100)).toBe(475);
expect(calculateDiscount(9, 100)).toBe(855);
});
it('should apply 10% discount for quantities 10 or more', () => {
expect(calculateDiscount(10, 100)).toBe(900);
expect(calculateDiscount(100, 100)).toBe(9000);
});
it('should handle floating point prices correctly', () => {
expect(calculateDiscount(5, 99.99)).toBeCloseTo(474.953);
});
});الاختبار الذي كتبه الذكاء الاصطناعي كان سطحياً جداً. لقد غطى الحالات الأساسية، لكنه فشل في اختبار الحالات الحدية مثل الكمية السالبة أو الصفر، ولم يختبر التعامل مع الأرقام العشرية. هذه هي الفجوة الكبيرة بين كتابة اختبارات "تعمل" وكتابة اختبارات جيدة.
بعد أكثر من عام من التجارب المكثفة مع مختلف أدوات الذكاء الاصطناعي لكتابة الكود، توصلت إلى استنتاج واضح: هذه الأدوات ممتازة في كتابة كود سريع، لكنها فاشلة في كتابة كود جيد. الفرق بين الاثنين هو الفرق بين مشروع ناجح وآخر فاشل. الكود السريع هو الذي يعمل اليوم، أما الكود الجيد فهو الذي سيستمر في العمل بعد عامين، عندما يحتاج فريق آخر إلى صيانته أو توسيعه.
الذكاء الاصطناعي اليوم لا يفهم المفاهيم الأساسية لهندسة البرمجيات مثل: قابلية الصيانة، الأداء، الأمان، وقابلية التوسع. إنه يكتب كوداً بناءً على الأنماط التي تعلمها من ملايين الأسطر من الكود الموجود على الإنترنت، وليس بناءً على فهم عميق للمشكلة أو النظام. وهذا يعني أنه سينتج كوداً يعمل في معظم الحالات، لكنه سيكون مليئاً بالمشاكل الخفية التي ستظهر لاحقاً.
على الرغم من كل هذه المشاكل، فإن للذكاء الاصطناعي مكاناً في عملية التطوير. إليك الحالات التي وجدتها مفيدة:
في المقابل، هناك حالات يجب فيها تجنب استخدام الذكاء الاصطناعي كلياً:
الذكاء الاصطناعي ليس بديلاً للمطورين، بل هو أداة قوية يمكن أن تزيد من إنتاجيتنا إذا استخدمناها بشكل صحيح. إليك نصيحتي الذهبية لاستخدامه بفعالية:
لا تطلب من الذكاء الاصطناعي كتابة الكود بدلاً منك، بل اطلب منه مساعدتك في كتابة الكود بشكل أفضل. استخدمه كزميل ذكي، وليس كمبرمج رئيسي.
— تجربتي الشخصية
إليك الخطوات العملية لاستخدام الذكاء الاصطناعي في كتابة الكود دون الوقوع في الفخاخ:
وأخيراً، تذكر دائماً أن الكود الجيد ليس مجرد كود يعمل، بل هو كود يمكن فهمه، وصيانته، وتوسيعه بسهولة. الذكاء الاصطناعي اليوم جيد في كتابة الكود الأول، لكنه فاشل في كتابة الكود الثاني. دورك كمطور هو استخدام الذكاء الاصطناعي كأداة لزيادة إنتاجيتك، وليس كبديل لقدراتك الهندسية.
في النهاية، الذكاء الاصطناعي في كتابة الكود يشبه المايكروويف في المطبخ: يمكنك استخدامه لتسخين الطعام بسرعة، لكنه لن يحل أبداً محل الطاهي الماهر الذي يفهم النكهات والمكونات وكيفية تفاعلها معاً. استخدمه بحكمة، وستجد أنه يمكن أن يكون إضافة قيمة لعملية التطوير الخاصة بك.