شعار ConstructiCat
CodeBust.
تصفح القسم ▾

التكرار بدلاً من الاستخراج.

عند كتابة كود جديد، يميل مساعدو الذكاء الاصطناعي إلى لصق نسخة معدلة قليلاً من منطق موجود بالفعل أو إعادة اشتقاقه بدلاً من استدعاء دالة مشتركة أو استخراجها، فيتضخم الكود المكرر مع كل ميزة جديدة.

##العلامات والأعراض

يتعرف المراجع على هذه الرائحة عندما يضيف ملف الفروقات (diff) المولد بواسطة الذكاء الاصطناعي كتلة برمجية موجودة بالفعل في مكان ما في المستودع بعد تعديل طفيف على اسمها، بدلاً من استدعاء الدالة المتوفرة بالفعل. النمط المميز هو وجود متنين أو أكثر شبه متطابقين يختلفان فقط في اسم الكيان (مثل user/order)، أو نقطة نهاية، أو قيمة ثابتة — وهو نوع النسخ الذي كان المطور البشري ليستخرجه في دالة مشتركة.

العلامات الشائعة:

  • معالجات أو خدمات جديدة تكرر نفس عمليات التحقق، أو إعادة المحاولة، أو تحويل الأخطاء، أو الكود الجاهز لعمليات جلب البيانات حرفياً.
  • إعادة كتابة السلاسل النصية السحرية والثوابت البرمجية في كل كتلة جديدة بدلاً من الإشارة إليها من مكان واحد.
  • كل محادثة مع الوكيل البرمجي تعيد تطبيق منطق كتبته المحادثة السابقة بالفعل (ليس لدى الوكيل ذاكرة بوجود دالة مساعدة).
  • يُبلغ مكتشف الاستنساخ (jscpd أو PMD CPD أو مقاييس التكرار في Sonar) عن كتل مكررة جديدة — أو عن قفزة في duplicated_lines_density — في طلب الدمج (PR) الذي أضاف الميزة.
// أضاف الذكاء الاصطناعي هذا للمسار الجديد — لكن getUser موجود بالفعل قبل 30 سطراً
async function getOrder(id: string) {
  const res = await fetch(`${API}/orders/${id}`, { headers: authHeaders() });
  if (!res.ok) throw new Error(`Request failed: ${res.status}`);
  const json = await res.json();
  if (!json?.data) throw new Error("Malformed response");
  return json.data;
}

async function getUser(id: string) {                 // <-- متن متطابق بنسبة 95%
  const res = await fetch(`${API}/users/${id}`, { headers: authHeaders() });
  if (!res.ok) throw new Error(`Request failed: ${res.status}`);
  const json = await res.json();
  if (!json?.data) throw new Error("Malformed response");
  return json.data;
}

هذه هي رائحة الكود المكرر الكلاسيكية، لكنها تُنتج بشكل منهجي بدلاً من أن تحدث بالصدفة.

رائحة قريبة لكنها مختلفة: تجنب إعادة الهيكلة تتعلق بعدم إعادة تنظيم الكود الموجود أبداً — إذ يُضاف السلوك الجديد فوقه بدلاً من إعادة تشكيل التصميم. أما هذه الرائحة فأضيق نطاقاً وتحدث لحظة كتابة الكود: يعيد النموذج اشتقاق منطق له مكانه بالفعل بدلاً من استدعائه أو استخراجه.

##أسباب المشكلة

لماذا تنتجها النماذج؟

  • محلية الرمز التالي (Next-token locality). يكمل نموذج اللغة الكبيرة المقطع الحالي بناءً على النص الأكثر تشابهاً في نافذة السياق الخاصة به. إن إعادة إنتاج كتلة قريبة معروفة بصحتها هو التكملة الأكثر احتمالاً إحصائياً؛ أما إيقاف الكتابة والذهاب لتعريف دالة مساعدة ثم استدعائها فهو مسار أطول وأقل احتمالاً يتطلب التنقل بين ملفات متعددة.
  • غياب نموذج على مستوى المستودع بأكمله. نادراً ما يعرف المساعد بوجود دالة مساعدة مناسبة خارج سياقه. وبدون استرجاع للمعلومات (retrieval)، لا يمكنه إعادة استخدام ما لا يستطيع رؤيته، لذا يقوم بإعادة توليده. ويربط Eno Reyes من Factory فائدة الذكاء الاصطناعي بجودة قاعدة الكود نفسها: "كلما ارتفعت جودة قاعدة الكود، زاد تسريع الذكاء الاصطناعي للمؤسسة" (مقابلة Stack Overflow) — والنسخ المكررة الموجودة أصلاً في المستودع تصبح النمط الذي ينسخه التوليد التالي مجدداً.
  • فقدان الذاكرة بين المحادثات + نطاق الأمر الموجه. يتم التعامل مع كل طلب كوحدة قائمة بذاتها ("اجعل هذا يعمل")، وبالتالي يفرط الوكيل البرمجي في تخصيص حل ضيق أحادي الاستخدام بدلاً من التعميم. تسمي OX Security هذا الإفراط في التخصيص (Over-Specification) — "حلول شديدة التخصيص أحادية الاستخدام لا يمكن تكييفها لأغراض أخرى" — ووجدته في 80-90% من الكود المولد بالذكاء الاصطناعي الذي درسته (تقرير OX).
  • التملق / المسار الأقل مقاومة. تعمل النماذج على تحسين المخرجات لتلبي الطلب بشكل مرئي دون لمس الملفات غير ذات الصلة. نسخ الكتلة يبقى داخل الملف قيد التحرير؛ أما استدعاء دالة مساعدة مشتركة — أو استخراجها — فيعني الوصول إلى وحدة أخرى لم يرها النموذج بالكامل، لذا تفوز النسخة المحلية.
  • الأدوات تجعل التكرار رخيصاً. كما يشير ملخص DevClass لتقرير GitClear، فإن قبول الاقتراح بمفتاح Tab يجعل إدراج كتلة جديدة شبه مجاني، مما يزيل المقاومة التي كانت تدفع المطورين سابقاً نحو إعادة الاستخدام (DevClass).

لماذا يسبب ضرراً؟

  • تآكل الجودة المقاس. وجد تحليل GitClear لعام 2025، الذي شمل 211 مليون سطر معدَّل (2020–2024)، أن نسبة الأسطر المرتبطة بإعادة الهيكلة (الأسطر "المنقولة") انخفضت من 25% من الأسطر المعدلة في 2021 إلى أقل من 10% في 2024، بينما ارتفعت نسبة الأسطر المنسوخة/الملصقة من 8.3% إلى 12.3% خلال الفترة نفسها؛ وكان عام 2024 أول عام تتجاوز فيه الأسطر المنسوخة/الملصقة الأسطرَ المنقولة، كما تضاعف عدد كتل الكود التي تضم خمسة أسطر مكررة أو أكثر نحو ثماني مرات خلال 2024 (GitClear 2025).
  • دين الصحة — "أخطاء مكررة مألوفة (Bugs Déjà-Vu)". وجدت Ox Security هذا في 70-80% من أكواد الذكاء الاصطناعي: الخطأ الذي يتم إصلاحه في نسخة مستنسخة يظل حياً وصامتاً في كل النسخ الأخرى، مما يؤدي إلى تكرار نفس العيوب وحاجة كل منها إلى إصلاح منفصل. التكرار يحول الخطأ الواحد إلى عدة أخطاء.
  • قابليته الصيانة وعبء المراجعة. يجب العثور على كل تعديل سلوكي وتعديله في أماكن متعددة؛ وتتضخم الفروقات (diffs) بكتل شبه متطابقة يتعين على المراجعين فحصها سطراً بسطر للتأكد من أنها متطابقة بالفعل (وليس بينها اختلافات طفيفة وخطيرة).
  • مساحة التعرض الأمني. ينتشر النمط البرمجي الضعيف (مثل غياب فحص الصلاحيات، أو عدم تنقية المدخلات) في كل نسخة، وقد يغفل إصلاح أمني لاحق بعض النسخ المكررة — فالتكرار يضاعف المساحة المطلوب ترقيعها أمنياً.

##المعالجة

تكتيكات المراجعة وصياغة الأوامر

  • أخبر النموذج بمكان البحث أولاً: "قبل كتابة كود جديد، ابحث في المستودع عن دالة مساعدة حالية تقوم بهذا وأعد استخدامها؛ لا تكرر الكود". ألصق الوحدة البرمجية ذات الصلة بحيث تكون في سياق النموذج.
  • اطلب الدمج بوضوح: "إذا كانت كتلتان تختلفان فقط في المعاملات، فاستخرج دالة واحدة ذات معاملات واستدعها في كلا المكانين". أي استخراج الدالة ثم جعل التابع يقبل معاملات.
  • أجبره على تشغيل الأدوات: "شغّل أداة الفحص / jscpd / فحص التكرار وحل أي استنساخ يبلغ عنه قبل إرجاع الفروقات (diff)". يصحح الوكلاء البرمجيون أنفسهم بشكل ممتاز عندما يُمنحون إشارة حتمية.
  • في المراجعة، تعامل مع الكتل التي تبدو منسوخة كدافع للبحث (grep): ابحث عن سطر مميز واحد؛ وإذا كان موجوداً بالفعل، فأعد الكود للوكيل لاستخراجه.

إعادة الهيكلة — استخراج الدالة + جعل التابع يقبل معاملات

// بعد: مصدر واحد للحقيقة، يتم استدعاؤه من الطرفين
async function getResource<T>(path: string): Promise<T> {
  const res = await fetch(`${API}/${path}`, { headers: authHeaders() });
  if (!res.ok) throw new Error(`Request failed: ${res.status}`);
  const json = await res.json();
  if (!json?.data) throw new Error("Malformed response");
  return json.data as T;
}

const getOrder = (id: string) => getResource<Order>(`orders/${id}`);
const getUser  = (id: string) => getResource<User>(`users/${id}`);

الآن، أي إصلاح لمعالجة الأخطاء أو تحليل الاستجابة يتم في مكان واحد فقط. وحين تختلف النسخ المكررة في خطوة من خطوات المعالجة بدلاً من قيمة، يُفضَّل استخدام تكوين دالة القالب أو تمرير الخطوة المتغيرة كدالة استدعاء (callback). الحركات المعتمدة هنا هي استخراج الدالة، واستخراج المتغير أو استبدال الرقم السحري بثابت رمزي (للقيم الحرفية المعاد كتابتها)، ورفع التابع عندما تعيش النسخ المستنسخة في كلاسات شقيقة — وكلها علاجات قياسية لـالكود المكرر.

حاجز حماية، وليس مجرد تنظيف: اربط مكتشف استنساخ الكود في خادم التكامل المستمر (CI) بحد منخفض بحيث يفشل بناء الكود عند توليد الذكاء الاصطناعي للتكرار التالي بدلاً من دمجه. اربطه بقاعدة منزلية مكونة من سطر واحد في CLAUDE.md/AGENTS.md ("أعد استخدام الدوال المساعدة الحالية؛ ولا تلصق كوداً شبه مكرر أبداً") لكي ترافق هذه القيود سياق النموذج في كل دورة محادثة.

##أدوات الكشف

  • jscpd --min-tokens / --min-lines / --threshold — تكرار النسخ/اللصق (حد أدنى قابل للتعديل للرموز/السطور؛ يدعم بوابة التقييم --threshold في CI)
  • PMD CPD — CPD (مكتشف النسخ/اللصق) — كشف الاستنساخ المبني على الرموز عبر أكثر من 30 لغة
  • SonarQube / SonarSource S4144 — S4144 — يجب ألا تمتلك الأساليب تطبيقات متطابقة
  • SonarQube / SonarSource S1192 — S1192 — يجب ألا تتكرر السلاسل النصية الحرفية
  • SonarQube duplicated_lines_density — الكتل المكررة / مقياس كثافة السطور المكررة (محرك استنساخ مدمج، يفشل بوابة الجودة عند تخطي الحد المسموح به)
  • eslint-plugin-sonarjs sonarjs/no-identical-functions — sonarjs/no-identical-functions
  • eslint-plugin-sonarjs sonarjs/no-duplicate-string — sonarjs/no-duplicate-string