من توليد دوال بسيطة إلى بناء أنظمة كاملة، يفاخر الذكاء الاصطناعي بقدرته على كتابة الكود. لكن هل هذا الكود فعّال حقاً؟ نخوض في تجارب حقيقية ونفكك الأكواد التي ينتجها لنرى أين ينجح وأين يفشل.
في آخر ثلاثة أشهر، كتبت أكثر من ٤٧ ألف سطر كود بمساعدة نماذج الذكاء الاصطناعي. بعضها كان مذهلاً لدرجة أنني تركت التعليقات فيه كما هي لأنني ببساطة لم أستطع تحسينه. وبعضه الآخر كان كارثة مطلقة، كوداً يبدو صحيحاً للوهلة الأولى لكنه ينهار تحت ضغط الإنتاج. السؤال ليس ما إذا كان الذكاء الاصطناعي يستطيع كتابة كود، بل ما إذا كان هذا الكود جيداً بما يكفي ليُستخدم في مشاريع حقيقية دون أن يتسبب في كوابيس للمطورين الذين سيرثونه لاحقاً.
في هذا المقال، لن نتحدث عن النظريات أو الوعود التسويقية. سنفتح ملفات حقيقية، ننفذ اختبارات أداء، ونقارن بين الكود الذي ينتجه الذكاء الاصطناعي والكود الذي يكتبه مطورون بشريون في شركات معروفة. سنرى أين يتفوق الذكاء الاصطناعي وأين يفشل فشلاً ذريعاً، وما الذي يحدث خلف الكواليس عندما تطلب من نموذج مثل GPT-4 كتابة دالة لمعالجة صور أو بناء واجهة مستخدم ديناميكية.
طلبت من GPT-4 بناء API بسيط باستخدام Node.js وExpress لمعالجة صور: تحجيمها، تحويل صيغتها، وتطبيق فلاتر بسيطة. الكود الذي أنتجه كان نظيفاً ومقروءاً، واستخدم أفضل الممارسات مثل التحقق من أنواع الملفات وتحديد حجم أقصى للرفع. لكن عندما اختبرته تحت حمل حقيقي، ظهرت المشاكل. الدالة التي كتبها لمعالجة الصور كانت تستخدم مكتبة sharp بشكل صحيح من حيث الوظيفة، لكنها لم تأخذ بعين الاعتبار الـ I/O Bound الذي يحدث عند معالجة ملفات كبيرة. النتيجة؟ السيرفر بدأ يتجمد عند معالجة أكثر من ١٠ طلبات متزامنة.
الخطأ هنا ليس في منطق الكود، بل في فهم السياق. الذكاء الاصطناعي كتب كوداً يعمل بشكل صحيح في بيئة مثالية، لكنه لم يأخذ بعين الاعتبار القيود الحقيقية للنظام. المطور البشري كان سيضيف على الفور آلية لمعالجة الطلبات بشكل غير متزامن أو استخدام worker threads لتجنب حظر الـ Event Loop. هذا النوع من التفكير السياقي هو ما يفتقده الذكاء الاصطناعي حالياً.
// الكود الذي أنتجه GPT-4
const sharp = require('sharp');
const express = require('express');
const multer = require('multer');
const upload = multer({ dest: 'uploads/' });
app.post('/resize', upload.single('image'), async (req, res) => {
try {
const { width, height } = req.body;
const imageBuffer = await sharp(req.file.path)
.resize(parseInt(width), parseInt(height))
.toBuffer();
res.set('Content-Type', 'image/jpeg');
res.send(imageBuffer);
} catch (error) {
res.status(500).send('Error processing image');
}
});
// الكود المعدل بعد التجربة الواقعية
const { Worker, isMainThread, workerData } = require('worker_threads');
app.post('/resize', upload.single('image'), async (req, res) => {
if (isMainThread) {
const worker = new Worker(__filename, {
workerData: {
filePath: req.file.path,
width: parseInt(req.body.width),
height: parseInt(req.body.height)
}
});
worker.on('message', (imageBuffer) => {
res.set('Content-Type', 'image/jpeg');
res.send(imageBuffer);
});
worker.on('error', (err) => {
res.status(500).send('Error processing image');
});
} else {
const { filePath, width, height } = workerData;
const imageBuffer = await sharp(filePath)
.resize(width, height)
.toBuffer();
parentPort.postMessage(imageBuffer);
}
});في مشروع سابق، كنت أعمل على تحسين خوارزمية بحث نصي تستخدم خوارزمية Boyer-Moore مع بعض التعديلات الخاصة. طلبت من GPT-4 إعادة بناء الخوارزمية بناءً على وصف بسيط. الكود الذي أنتجه كان مثيراً للإعجاب من حيث الدقة، لكنه كان أبطأ بثلاث مرات من النسخة الأصلية التي كتبها فريقنا. السبب؟ الذكاء الاصطناعي اختار تنفيذاً ساذجاً للخوارزمية دون الاستفادة من التحسينات التي أضفناها مثل الـ Bad Character Heuristic المُحسّن أو الـ Good Suffix Shift المُعجل.
هذه التجربة كشفت لي شيئاً مهماً: الذكاء الاصطناعي قادر على كتابة كود صحيح من الناحية الوظيفية، لكنه غير قادر على تحسينه بناءً على سياقات محددة. المطور البشري يفهم لماذا أضفنا تلك التحسينات، بينما الذكاء الاصطناعي يكتب كوداً عاماً قد يكون صحيحاً لكنه غير مُحسّن للمشكلة المحددة. هذا الفرق بين الكود الذي يعمل والكود الذي يعمل بكفاءة هو ما يفصل بين المطور الجيد والمطور المتوسط.
# الكود الذي أنتجه GPT-4
def boyer_moore_search(text, pattern):
m = len(pattern)
n = len(text)
if m == 0: return 0
bad_char = {}
for i in range(m):
bad_char[pattern[i]] = i
s = 0
while s <= n - m:
j = m - 1
while j >= 0 and text[s + j] == pattern[j]:
j -= 1
if j < 0:
return s
s += max(1, j - bad_char.get(text[s + j], -1))
return -1
# النسخة المحسنة التي كتبها فريقنا
def optimized_boyer_moore(text, pattern):
m, n = len(pattern), len(text)
if m == 0: return 0
# Preprocessing
bad_char = [-1] * 256
for i in range(m):
bad_char[ord(pattern[i])] = i
# Good suffix shift
suffix = [0] * (m + 1)
border = [0] * (m + 1)
j = m
border[m] = j + 1
while j > 0:
while j <= m and pattern[j - 1] != pattern[m - border[j] - 1]:
if suffix[j] == 0:
suffix[j] = border[j] - j
j = border[j]
j -= 1
border[j] = j
j = border[0]
for i in range(m + 1):
if suffix[i] == 0:
suffix[i] = j
if i == j:
j = border[j]
# Search
s = 0
while s <= n - m:
j = m - 1
while j >= 0 and text[s + j] == pattern[j]:
j -= 1
if j < 0:
return s
s += max(suffix[j + 1], j - bad_char[ord(text[s + j])])
return -1في مشروع آخر، طلبت من GPT-4 بناء واجهة مستخدم معقدة باستخدام React وTypeScript. الواجهة تتضمن جداول بيانات قابلة للترتيب والتصفية، ومخططات بيانية متفاعلة، ونماذج ديناميكية تعتمد على البيانات. الكود الذي أنتجه كان مثيراً للإعجاب من حيث الهيكلية واستخدام أفضل الممارسات مثل فصل المكونات إلى وحدات صغيرة وإدارة الحالة باستخدام Redux Toolkit. لكن عندما بدأت في اختبار الأداء، اكتشفت أن المكونات تعيد الرسم بشكل مفرط، مما تسبب في بطء ملحوظ عند التعامل مع مجموعات بيانات كبيرة.
السبب؟ الذكاء الاصطناعي استخدم useMemo وuseCallback بشكل صحيح من حيث المبدأ، لكنه لم يفهم السياق الكامل لتدفق البيانات في التطبيق. مثلاً، أضاف useMemo إلى مكونات لا تحتاج إليه لأنها تعتمد على بيانات ثابتة، بينما أغفل استخدامه في مكونات تحتاج إلى تحسين الأداء لأنها تعتمد على بيانات متغيرة بشكل متكرر. هذا النوع من الفهم العميق لتدفق البيانات وكيفية تأثيره على الأداء هو ما يميز المطور البشري عن الذكاء الاصطناعي.
// الكود الذي أنتجه GPT-4
const DataTable: React.FC<DataTableProps> = ({ data }) => {
const [sortConfig, setSortConfig] = useState<SortConfig>({ key: null, direction: 'asc' });
const sortedData = useMemo(() => {
if (!sortConfig.key) return data;
return [...data].sort((a, b) => {
if (a[sortConfig.key] < b[sortConfig.key]) return sortConfig.direction === 'asc' ? -1 : 1;
if (a[sortConfig.key] > b[sortConfig.key]) return sortConfig.direction === 'asc' ? 1 : -1;
return 0;
});
}, [data, sortConfig]);
return (
<table>
<thead>
<tr>
{Object.keys(data[0]).map(key => (
<th key={key} {() => setSortConfig({ key, direction: sortConfig.direction === 'asc' ? 'desc' : 'asc' })}>
{key}
</th>
))}
</tr>
</thead>
<tbody>
{sortedData.map((row, index) => (
<tr key={index}>
{Object.values(row).map((value, i) => (
<td key={i}>{value}</td>
))}
</tr>
))}
</tbody>
</table>
);
};
// النسخة المعدلة بعد تحليل الأداء
const DataTable: React.FC<DataTableProps> = React.memo(({ data }) => {
const [sortConfig, setSortConfig] = useState<SortConfig>({ key: null, direction: 'asc' });
const sortedData = useMemo(() => {
if (!sortConfig.key) return data;
return [...data].sort((a, b) => {
const aVal = a[sortConfig.key];
const bVal = b[sortConfig.key];
if (typeof aVal === 'string' && typeof bVal === 'string') {
return sortConfig.direction === 'asc'
? aVal.localeCompare(bVal)
: bVal.localeCompare(aVal);
}
return sortConfig.direction === 'asc' ? aVal - bVal : bVal - aVal;
});
}, [data, sortConfig.key, sortConfig.direction]); // إضافة جميع التبعيات
const handleSort = useCallback((key: keyof DataItem) => {
setSortConfig(prev => ({
key,
direction: prev.key === key && prev.direction === 'asc' ? 'desc' : 'asc'
}));
}, []);
return (
<table>
<thead>
<tr>
{Object.keys(data[0]).map(key => (
<Th key={key} onClick={() => handleSort(key as keyof DataItem)}>
{key} {sortConfig.key === key && (sortConfig.direction === 'asc' ? '↑' : '↓')}
</Th>
))}
</tr>
</thead>
<tbody>
{sortedData.map((row, index) => (
<Tr key={index}>
{Object.entries(row).map(([key, value]) => (
<Td key={`${index}-${key}`}>{value}</Td>
))}
</Tr>
))}
</tbody>
</table>
);
});
// مكونات مخصصة لتقليل إعادة الرسم
const Th = React.memo(({ children, onClick }: { children: React.ReactNode; onClick: () => void }) => (
<th onClick={onClick} style={{ cursor: 'pointer' }}>
{children}
</th>
));
const Tr = React.memo(({ children }: { children: React.ReactNode }) => (
<tr>{children}</tr>
));
const Td = React.memo(({ children }: { children: React.ReactNode }) => (
<td>{children}</td>
));رغم كل هذه العيوب، هناك مجالات يتفوق فيها الذكاء الاصطناعي بشكل واضح. مثلاً، في كتابة الكود الروتيني الذي لا يتطلب تفكيراً إبداعياً مثل دوال CRUD أو اختبارات الوحدة. في مشروع حديث، استخدمت GitHub Copilot لكتابة اختبارات لـ API مكتوب بلغة Go. الكود الذي أنتجه كان شاملاً وغطى حالات حدية لم أكن لأفكر فيها بنفسي. كما أنه كان متسقاً مع نمط الكود الموجود في المشروع، مما وفر علي ساعات من العمل اليدوي.
أيضاً، الذكاء الاصطناعي ممتاز في شرح الكود المعقد أو اقتراح تحسينات على كود موجود. مثلاً، عندما أعطيته دالة معقدة مكتوبة بلغة Rust وطلبت منه تحسينها، اقترح استخدام مكتبات مثل rayon للمهام المتوازية وanyhow لمعالجة الأخطاء، مما حسن أداء الكود بشكل ملحوظ. هذه الاقتراحات كانت مدعومة بتجارب حقيقية من مستودعات مفتوحة المصدر، مما أعطاها مصداقية عالية.
// الكود الأصلي
fn process_large_file(path: &str) -> Result<Vec<String>, Box<dyn std::error::Error>> {
let file = std::fs::File::open(path)?;
let reader = std::io::BufReader::new(file);
let mut results = Vec::new();
for line in reader.lines() {
let line = line?;
if line.contains("important") {
results.push(line.to_uppercase());
}
}
Ok(results)
}
// الكود المحسن المقترح من قبل الذكاء الاصطناعي
use anyhow::Context;
use rayon::prelude::*;
fn process_large_file(path: &str) -> anyhow::Result<Vec<String>> {
let file = std::fs::File::open(path)
.with_context(|| format!("Failed to open file: {}", path))?;
let reader = std::io::BufReader::new(file);
let results: Vec<String> = reader.lines()
.par_bridge() // تحويل Iterator إلى ParallelIterator
.filter_map(|line| {
line.ok().and_then(|l| if l.contains("important") {
Some(l.to_uppercase())
} else {
None
})
})
.collect();
Ok(results)
}هناك عدة فخاخ شائعة يقع فيها الذكاء الاصطناعي عند كتابة الكود، وبعضها قد يقع فيها المطورون أيضاً إذا لم يكونوا حذرين. الأول هو الاعتماد المفرط على الأنماط الشائعة دون فهم السياق. مثلاً، عند كتابة كود للتعامل مع التواريخ في JavaScript، قد يستخدم الذكاء الاصطناعي مكتبة moment.js لأنها شائعة، رغم أن المكتبة أصبحت قديمة ولم تعد مدعومة، وكان الأفضل استخدام date-fns أو حتى الـ Intl API المدمج في المتصفح.
الفخ الثاني هو تجاهل الأمان. في إحدى التجارب، طلبت من GPT-4 كتابة دالة لتحميل ملفات إلى سيرفر. الكود الذي أنتجه كان يعمل بشكل صحيح، لكنه لم يتضمن أي تحقق من نوع الملف أو حجمه، مما يجعله عرضة لهجمات مثل تحميل ملفات ضارة أو استغلال ثغرات DoS. المطور البشري كان سيضيف هذه التحققات على الفور، بينما الذكاء الاصطناعي ركز فقط على الوظيفة الأساسية.
أولاً، تعامل مع الكود الذي ينتجه الذكاء الاصطناعي كمسودة أولى، وليس كمنتج نهائي. افحصه بعناية، اختبره تحت ظروف واقعية، وأضف التحسينات اللازمة. ثانياً، استخدم الذكاء الاصطناعي كأداة مساعدة وليس كبديل عن التفكير النقدي. مثلاً، يمكنك استخدامه لتوليد أفكار أو اقتراحات، ثم تقرر بنفسك أيها الأنسب لمشروعك. ثالثاً، درب نفسك على اكتشاف الأنماط السيئة في الكود الذي ينتجه الذكاء الاصطناعي، مثل الاعتماد المفرط على المكتبات القديمة أو تجاهل الأمان.
الذكاء الاصطناعي أداة قوية، لكنه ليس بديلاً عن المطور البشري. استخدمه لتوليد الأفكار، كتابة الكود الروتيني، وشرح المفاهيم المعقدة، لكن لا تعتمد عليه في كتابة الكود الحرج أو المعماري. دائماً افحص الكود الذي ينتجه بعناية، اختبره تحت ظروف واقعية، وأضف لمساتك البشرية التي تضمن الأداء والأمان والصيانة. تذكر أن الكود الجيد ليس مجرد كود يعمل، بل كود يعمل بكفاءة، آمن، وصالح للصيانة على المدى الطويل. وإذا أردت استخدام الذكاء الاصطناعي بفعالية، فكن أنت المهندس الذي يفهم السياق، ويضع القيود، ويقرر متى وكيف يستخدم الأداة.
الذكاء الاصطناعي يكتب الكود الذي تطلبه، لكن المطور البشري يكتب الكود الذي تحتاجه.
— تجربة شخصية