التجريد الضحل.
يقوم مساعد الذكاء الاصطناعي بتغليف الكود في دوال أو كلاسات أو واجهات (interfaces) إضافية تزيد من مستويات غير المباشرة (indirection) دون إخفاء أي تعقيد أو تمكين إعادة الاستخدام — وهي نماذج مجردة تقوم بمجرد تمرير الاستدعاء إلى موضع استدعاء واحد.
##Signs and Symptoms
يتعرف المراجع على التجريد الضحل عندما تضيف دالة أو كلاس أو واجهة (interface) جديدة اسماً وطبقة ولكن دون أي فائدة حقيقية: حيث يكرر متنها توقيعها (signature)، أو تمرر الاستدعاء مباشرة إلى استدعاء أساسي واحد، أو تُستدعى مرة واحدة بالضبط، أو لا تخفي أي قرار أو قاعدة ثابتة أو تنوع. يبدو الكود متعدد الطبقات و"بمستوى للمؤسسات"، ولكن يتعين عليك القراءة عبر كل طبقة لتفهم أي شيء — فالتجريد لا يغلف شيئاً.
العلامات المميزة:
- دوال/أساليب تمريرية (Pass-through) يتكون متنها من استدعاء تفويض واحد دون إضافة أي سلوك.
- كلاسات مغلفة (مثل
FooService←FooRepository←db) حيث تقوم كل طبقة بمجرد استدعاء الطبقة التالية. - واجهات ذات تطبيق واحد (مثل واجهة
IClockمع كلاسSystemClockفقط ككلاس مطبق لها) تم تقديمها بشكل تخميني "من أجل قابلية الاختبار" دون وجود تطبيق ثانٍ أو وهمي (fake). - دوال في ملفات
utils/helpersتُستخدم في مكان واحد بالضبط. - الأسماء المستعارة أو عمليات إعادة التسمية المكررة التي تمرر قيمة دون تغيير.
هذه هي رائحة **الوسيط (Middle Man)** / **الكلاس الكسول (Lazy Class)** / **العمومية التخمينية (Speculative Generality)** الكلاسيكية، والوجه الآخر لنتيجة Ox Security المتمثلة في "الإفراط في التخصيص" (الحلول ضيقة النطاق وأحادية الاستخدام بدلاً من المكونات القابلة للتعميم).
// أضاف الذكاء الاصطناعي ثلاث "طبقات" تقوم كل منها بالتمرير إلى الطبقة التالية.
function getUserName(id: string) {
return fetchUserName(id); // لا يضيف شيئاً
}
function fetchUserName(id: string) {
return userRepository.getName(id); // لا يضيف شيئاً
}
class UserRepository {
getName(id: string) {
return db.query("SELECT name FROM users WHERE id = ?", [id]);
}
}
// getUserName هو موقع الاستدعاء الوحيد، ويستخدم مرة واحدة.
// اثنتان من الطبقات الثلاث لا تضيفان أي سلوك — وسيط خالص (pure Middle Man).
##Reasons for the Problem
لماذا تنتجها النماذج؟
- تقليد شكل البنية البرمجية الجيدة. تمتلئ مجموعات بيانات التدريب بالدروس والأكواد الهيكلية للمؤسسات — مثل طبقات الخدمة/المستودع/المصنع، وحقن التبعيات (DI)، والواجهات ذات التطبيق الواحد. يطابق النموذج المظاهر الخارجية للتجريد دون الجوهر الفعلي، لأن الدالة المساعدة المسماة تبدو مقبولة ومحترفة محلياً. يعمل التنبؤ بالرمز التالي على تحسين الكود ليتشابه مع كود جيد البنية، وليس للتحقق مما إذا كانت هذه الطبقة تؤدي وظيفة حقيقية.
- غياب سياق المستودع. عند التوليد محلياً، لا يستطيع النموذج رؤية أن الدالة المساعدة تُستدعى مرة واحدة فقط، أو أن هناك تجريداً معتمداً موجوداً بالفعل في مكان آخر — فيبتكر طبقات ضحلة جديدة بدلاً من إعادة استخدامها. هذا يطابق تماماً الاتجاه المقاس لتقرير GitClear: حيث انخفضت السطور المعاد هيكلتها ("المنقولة") من ~24% (2020) إلى أقل من 10% (2024) بينما ارتفعت السطور المنسوخة/الملصقة ونمت النسخ المكررة بمعدل ~4 مرات. تضيف النماذج هيكلية ولكنها لا تدمجها أبداً.
- الهوس بالحلول المألوفة + الإفراط في التخصيص. وجدت Ox Security أن الذكاء الاصطناعي يفرط في التخصيص في 80-90% من الأكواد المولدة، مما ينتج "حلولاً ضيقة لا يمكن إعادة استخدامها"، ويتجنب إعادة الهيكلة في 80-90% ("يتوقف عند الحل المقبول للوهلة الأولى"). الأغلفة الضحلة هي المخلفات المرئية لذلك.
- حرفية التعامل مع الأمر الموجه والتملق. عند توجيه النموذج لـ "إضافة طبقة خدمة" أو "جعل الكود نظيفاً/وحداتياً"، يضيف النموذج طبقات حرفياً حتى عندما لا تكون هناك حاجة إليها (مبدأ YAGNI) — وهو ميل للإفراط في الهندسة يصفه المطورون في مناقشة روائح الكود لوكلاء الذكاء الاصطناعي.
- تاريخ انتهاء التدريب (Training-cutoff staleness). يقوم النموذج بتغليف واجهة برمجة تطبيقات مهملة في محول ضيق بدلاً من الانتقال منها.
لماذا يسبب ضرراً؟
- قابلية الصيانة وعبء المراجعة. يجب أن يمر كل تغيير عبر طبقات ميتة؛ ويتعين على القارئ القراءة عبر فئة الوسيط (Middle Man). فكرة Ox حول "جيش من المطورين المبتدئين": لا يمكن للمراجعين مواكبة الكود الذي يبدو مهيكلاً ولكن ليس له غرض حقيقي.
- الصحة والأمان. تشير دراسات arXiv 2509.20491 و SonarSource إلى أن الأسماء المستعارة والأغلفة "تحجب مواقع الاستدعاء الحاسمة"، مما يقلل من جودة تحليل تدفق البيانات/التحكم والتحليل الأمني — فالغلاف يمكن أن يبتلع خطأً بصمت أو يخفي موقع ثغرة أمنية عن الماسح.
- تراكم الديون التقنية. تتصلب الواجهات ذات التطبيق الواحد التخمينية؛ ولأن كل تجريد ضحل يكون أحادي الاستخدام، فإن التعديل التالي يتم نسخه ولصقه بدلاً من جعله ذا معاملات — مما يغذي مباشرة نمو النسخ المكررة الذي رصدته GitClear. أنت تدفع ثمن هذا التعقيد دون الحصول على إعادة الاستخدام التي وُعدت بها.
##Treatment
تكتيكات المراجعة وصياغة الأوامر
- طبق **قاعدة الثلاثة**: "لا تضف دالة/كلاس/واجهة ما لم تُستخدم في مكانين أو أكثر أو تخفي قراراً حقيقياً/قاعدة ثابتة. قم بتضمين الدوال المساعدة أحادية الاستخدام (YAGNI)".
- فرض إعادة الاستخدام: "قبل إضافة دالة مساعدة/خدمة/واجهة، ابحث في المستودع عن واحدة موجودة وأعد استخدامها". وجه النموذج إلى الوحدة المرجعية.
- اجعل النموذج يبرر وجود كل طبقة: "سرد كل دالة/كلاس جديد وبيّن التنوع أو القاعدة الثابتة التي يخفيها؛ واحذف أي منها يقوم بمجرد التمرير".
- اطلب منه **تشغيل أداة الفحص** (مثل قواعد
no-useless-constructor، وno-useless-rename) و**فحص التكرار** (jscpd) وإصلاح ما يحددونه من مشكلات.
إعادة الهيكلة (حددها باسمها): **تضمين الدالة (Inline Function)**، و**تضمين الكلاس (Inline Class)**، و**إزالة الوسيط (Remove Middle Man)** لطبقات التمرير؛ و**تقليص الهيكل الهرمي (Collapse Hierarchy)** / التخلي عن الواجهة للواجهات ذات التطبيق الواحد. وعندما يكون التكرار هو المحرك الفعلي، قم بـ **استخراج الدالة (Extract Function) لمرة واحدة** إلى دالة مساعدة مشتركة ذات معاملات — وليس كتابة مغلف لكل موقع استدعاء.
قبل — الوسيط (Middle Man):
function getUserName(id: string) { return fetchUserName(id); }
function fetchUserName(id: string) { return userRepository.getName(id); }
بعد — تضمين الدالة (Inline Function) / إزالة الوسيط (واحد من مواقع الاستدعاء، لم يُضف سلوك):
const name = userRepository.getName(id);
قبل — واجهة تخمينية بتطبيق واحد:
interface IClock { now(): number; }
class SystemClock implements IClock { now() { return Date.now(); } }
بعد — تقليص الهيكل الهرمي (استخدم النوع الملموس؛ أعد تقديم الواجهة فقط عند وجود تطبيق ثانٍ، مثل كائن وهمي للاختبار):
class SystemClock { now() { return Date.now(); } }
##Detected by
- ESLint no-useless-constructor — منشئ ممرر (Pass-through constructor) يقوم فقط بالتفويض إلى super أو يكون فارغاً
- ESLint no-useless-rename — استيراد/تصدير/تفكيك مكرر يقوم بإعادة تمرير الاسم دون تغيير
- PMD (Java) UselessOverridingMethod — دالة مفرطة (Overriding method) تقوم بمجرد استدعاء super مع نفس المعاملات
- SonarSource (Sonar S4144) Functions/methods should not have identical implementations — تمييز النماذج المجردة المكررة وأحادية الاستخدام ذات المتن المتطابق
- jscpd duplication threshold — يكتشف الدوال المساعدة أحادية الاستخدام وشبه المكررة التي تفشل التجريدات الضحلة في دمجها وتلخيصها