العجلة المعاد اختراعها.
يقوم النموذج بكتابة كود مخصص يدوياً للقيام بشيء تقوم به بالفعل المكتبة القياسية، أو تبعية مثبتة، أو دالة مساعدة متوفرة مسبقاً في المستودع — مما يضيف منطقاً مكرراً وأقل اختباراً بدلاً من استدعاء الكود الموجود بالفعل.
##Signs and Symptoms
يكتشف المراجع العجلة المعاد اختراعها عندما يقدم ملف الفروقات (diff) كتلة برمجية غير بسيطة مبنية "من الصفر" لحل مشكلة تم حلها بالفعل — سواء بواسطة بيئة تشغيل اللغة، أو تبعية موجودة بالفعل في package.json، أو أداة مساعدة متوفرة في مكان آخر في المستودع. العلامات الشائعة:
- كتابة يدوية لدوال مثل
deepClone، أوdebounce، أوgroupBy، أوchunk، أوretry، أوslugify، أوdeepMerge، أوuuidبينما توفرها بيئة التشغيل أو مكتبة مثبتة بالفعل. - استخدام تعبير نمطي (regex) مخصص للبريد الإلكتروني/رابط URL/معرف UUID بدلاً من استخدام أداة التحقق الموجودة مسبقاً كبنية معتمدة.
- حسابات تاريخ مخصصة، أو تحليل لسلاسل الاستعلام، أو منطق تقسيم الصفحات يعيد تطبيق ميزات
Intl/URLSearchParamsأو ميزات ORM. - وجود دالتين أو ثلاث دوال مساعدة خاصة شبه متطابقة متناثرة في الملفات (إعادة نحت نفس العجلة لكل ميزة) — وهو التكرار الذي يظهر في مقاييس النسخ/اللصق.
- النسخة المعاد اختراعها تكون خاطئة بمهارة: فهي تتعامل مع المسار السعيد وتغفل حالات الحافة التي يغطيها الكود الأصلي والمجرب.
// مكتوب بالذكاء الاصطناعي: نسخة عميقة مخصصة أعيد اختراعها بشكل مدمج (inline)
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') return obj;
if (Array.isArray(obj)) return obj.map(deepClone);
const out = {};
for (const k in obj) out[k] = deepClone(obj[k]);
return out; // يسقط بصمت Date و Map و Set و RegExp؛ ويدخل في حلقات لا نهائية عند وجود دورات
}
// ...على الرغم من أن بيئة التشغيل توفر structuredClone()، و
// المستودع يصدر بالفعل cloneDeep() من src/utils/object.ts
أسرع فحص: ابحث في المستودع ومجلد node_modules عن الميزة المطلوبة قبل قبول الكود الجديد. إذا كانت structuredClone، أو lodash، أو date-fns، أو دالة مساعدة محلية تغطيها، فإن الدالة الجديدة هي عجلة معاد اختراعها.
##Reasons for the Problem
لماذا تنتجها النماذج؟
- غياب سياق المستودع افتراضياً. غالباً لا يستطيع النموذج رؤية مجلد
src/utilsالخاص بك، أو تبعياتك المثبتة، أو اصطلاحاتك، لذلك يلجأ إلى التكملة الأكثر احتمالاً إحصائياً: وهي كتابة تطبيق مدمج وقائم بذاته. هو يعيد اختراع العجلة لأنه لم ير عجلتك أبداً. - محلية الرمز التالي. تقوم نماذج اللغة الكبيرة بالتحسين للحصول على تكملة معقولة محلياً، وليس للحصول على الحد الأدنى عالمياً. كتابة
function groupBy(...)هي سلسلة احتمالية عالية؛ أما التوقف لاكتشاف أنlodash.groupByمستوردة بالفعل في ملف مجاور فليس من طبيعة التنبؤ بالرموز. - حوافز التدريب تكافئ الاحتواء الذاتي للكود. نسبة ضخمة من بيانات التدريب عبارة عن دروس برمجية، وإجابات Stack Overflow، ومقاطع كود توضح التطبيق الكامل عمداً. لقد تعلم النموذج أن "الإجابة الجيدة" تعني تقديم كود كامل ومستقل — وهي الغريزة الخاطئة تماماً داخل قاعدة كود ناضجة.
- تاريخ انتهاء التدريب (Training-cutoff staleness). قد لا يعرف النموذج أن ميزة ما قد تمت ترقيتها إلى المكتبة القياسية (مثل
structuredClone، أوArray.prototype.at، أوObject.groupBy) أو أن مستودعك اعتمد دالة مساعدة بعد تاريخ انتهاء تدريبه، لذا يقوم بكتابة ما يماثلها يدوياً. - التملق / المسار الأقل مقاومة. عند الطلب من النموذج "إضافة X"، فإنه يضيف X بالطريقة الأكثر مباشرة بدلاً من الاعتراض بقوله "لدينا هذا بالفعل". نادراً ما يتطوع ليقول "لا تحتاج إلى كتابة هذا".
- الإفراط في التخصيص. وجد تحليل Ox Security لأكثر من 300 مستودع أن الذكاء الاصطناعي يميل نحو كتابة الأكواد بـ "الأسلوب التقليدي Vanilla Style" الذي يعيد بناء الوظائف الشائعة بدلاً من استخدام مكتبات مثبتة، مع تقديم حلول ضيقة النطاق وغير قابلة لإعادة الاستخدام في ما يقرب من 80-90% من الحالات — حيث تحصل كل عملية تعديل على كود جديد بدلاً من إعادة الاستخدام.
لماذا يسبب ضرراً؟
- قابلية الصيانة والديون التقنية. هذا هو الوجه الملموس لتراجع جودة كود الذكاء الاصطناعي. وجد تحليل GitClear لـ 211 مليون سطر أن الكود المنسوخ/الملصق ارتفع من 8.3% (2020) إلى 12.3% (2024)، ونمت كتل التكرار المكونة من 5 سطور أو أكثر بمعدل ~8 أضعاف في عام 2024، وانخفضت السطور المعاد هيكلتها ("المنقولة") من 24.1% إلى 9.5% — وكان عام 2024 هو العام الأول الذي يتجاوز فيه النسخ واللصق إعادة الهيكلة. العجلات المعاد اختراعها هي كيفية تسلل هذا التكرار.
- الصحة. تتخطى النسخة المكتوبة يدوياً حالات الحافة التي اكتسبتها النسخة الناضجة عبر سنوات من تقارير الأخطاء (مثل المناطق الزمنية، ورموز يونيكود، والدورات اللانهائية، وتنقية النصوص). تبدو صحيحة في الظاهر وتفشل في الحالات الخاصة.
- الأمان. إعادة كتابة التشفير، أو المصادقة، أو التنقية، أو التحقق من الصحة بدلاً من استخدام مكتبة معتمدة هي الطريقة التي ينتهك بها كود الذكاء الاصطناعي "أفضل الممارسات الهندسية" على نطاق واسع (تأثير "جيش من المطورين المبتدئين" الخاص بـ Ox) — حيث تصل الأنماط الضعيفة إلى الإنتاج بشكل أسرع مما يمكن للمراجعة التقاطه.
- عبء المراجعة. يتعين على المراجعين الآن قراءة وفهم واختبار 40 سطراً من المنطق المخصص بدلاً من التعرف على استدعاء مكتبة موثوق واحد — وهو ما يتكرر في كل طلب سحب (PR).
##Treatment
تكتيكات المراجعة وصياغة الأوامر
- امنح النموذج السياق الذي يفتقر إليه. قبل التوليد، وجهه إلى الدوال المساعدة والتبعيات لديك: "أعد استخدام الدوال المساعدة من
src/utils/*والمكتبات الموجودة بالفعل فيpackage.json؛ ولا تضف مكتبات جديدة دون إذن". الصق تبعياتpackage.jsonذات الصلة وفهرس الدوال المساعدة. - اجعل "البحث أولاً" قاعدة أساسية. وجّه النموذج: "قبل كتابة أي دالة مساعدة، تحقق مما إذا كانت المكتبة القياسية، أو تبعية حالية، أو أداة مساعدة في المستودع تقوم بذلك بالفعل؛ وإذا كان الأمر كذلك، فاستدعها". يجب على أدوات الوكلاء البرمجيين البحث (grep) في المستودع أولاً.
- تحدَّ كل دالة مساعدة خاصة جديدة في المراجعة. لكل أداة مكتوبة يدوياً اسأل: هل تقوم بيئة التشغيل بهذا؟ هل تقوم تبعية حالية بهذا؟ هل لدينا هذا بالفعل؟ إذا كانت الإجابة نعم، فهي عجلة معاد اختراعها — واستبدلها فوراً.
- شغّل سلسلة الأدوات. يكتشف ماسح التكرار (jscpd / PMD CPD / SonarQube) في خادم التكامل المستمر (CI) جانب النسخ/اللصق تلقائياً؛ اربطه ببوابة الفحص حتى تفشل العجلات المعاد نحتها في بناء المشروع.
إعادة الهيكلة — هذه هي رائحة الكود المكرر (Duplicate Code) وإعادة اختراع العجلة الكلاسيكية؛ والعلاج هو استبدال الخوارزمية (Substitute Algorithm) (تبديل المتن المكتوب يدوياً باستدعاء المكتبة/المعيار القياسي)، وحيثما توجد نسخ متعددة، استخدم استخراج الدالة (Extract Function) / الرفع إلى دالة مساعدة مشتركة واحدة.
// قبل — معاد اختراعه، صحيح جزئياً، مكرر لكل ميزة
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') return obj;
if (Array.isArray(obj)) return obj.map(deepClone);
const out = {};
for (const k in obj) out[k] = deepClone(obj[k]);
return out;
}
const copy = deepClone(state);
// بعد — استدعاء ما هو موجود بالفعل (تصحيح حالات الحافة، بدون كود جديد)
const copy = structuredClone(state);
// أو، إذا كان معيار المستودع هو المساعد المحلي:
import { cloneDeep } from "@/utils/object";
const copy = cloneDeep(state);
إذا تم إعادة اختراع العجلة في عدة ملفات، فاحذف جميع النسخ ووجه كل مستدعٍ عبر مصدر الحقيقة الوحيد. النتيجة الصافية: سطور كود أقل، أخطاء أقل، ومقاييس التكرار تتحرك في الاتجاه الصحيح.
##Detected by
- jscpd duplication — كشف النسخ/اللصق (حد أدنى للرموز / العتبة)
- PMD cpd — CPD (مكتشف النسخ واللصق)
- SonarQube common-duplications — الكتل المكررة / كثافة السطور المكررة