المبالغة الدفاعية.
يقوم مساعدو الذكاء الاصطناعي بالتحوط ضد حالات فشل لا يمكن حدوثها، مما يلتف حول الكود الآمن بالفعل بفحوصات null زائدة عن الحاجة، وجمل حماية ميتة، وكتل try/catch شاملة تزيد من التعقيد دون تقديم أي حماية فعلية.
##العلامات والأعراض
يكتشف المراجع المبالغة الدفاعية عندما تقضي الدالة عدداً من السطور في الحماية ضد حالات مستحيلة أكثر مما تقضيه في إنجاز العمل الفعلي. العلامات المميزة:
- جمل حماية لشروط يضمنها نظام الأنواع (Types) بالفعل — مثل فحوصات null/undefined لمعامل معلن بأنه غير قابل للقيمة null، أو
typeof x === "string"لمتغير من النوعstring. - جمل حماية مكررة أو مضمنة في شروط أخرى — مثل
if (!user) returnمتبوعة مباشرة بـif (user === null || user === undefined) return. - كتلة
try/catchشاملة حول كود لا يمكنه إطلاق استثناء، وغالباً ما تبتلع الخطأ أو تعيد إطلاقه مجدداً ("فقط للاحتياط"). - منطق للتعامل مع حالات حافة "وهمية" — فروع تتعامل مع مدخلات لا يمكن لأي مستدعٍ توليدها. ظهر نمط Phantom Bugs لدى OX Security — ذكاء اصطناعي "يبالغ في الهندسة لحالات حافة غير محتملة" — في 20-30% من الكود الذي حللته (تقرير OX).
- نسخ ولصق الكتل الدفاعية بدلاً من استخراجها، بحيث تظهر نفس جملة الحماية في خمسة أماكن مختلفة.
// User هو نوع غير قابل للقيمة null: { profile: { name: string } }
function getDisplayName(user: User): string {
if (!user) return "Unknown"; // ميت: user غير قابل للقيمة null
if (user === null || user === undefined) return "?"; // مكرر، وميت أيضاً
try {
if (user.profile && typeof user.profile.name === "string") { // يضمن النوع هذا بالفعل
const name = user.profile.name;
if (name.length > 0) {
return name.trim() !== "" ? name.trim() : "Unknown";
}
}
return "Unknown";
} catch (e) {
console.error("could not get name", e); // هذه الكتلة لا يمكن أن تطلق استثناءً
return "Unknown";
}
}
تسعة سطور من التحوط تحيط بسطر واحد من الهدف الفعلي. التناقض الذي يلاحظه المراجعون هو أن نفس النموذج غالباً ما يغفل الفحص الوحيد المهم (مثل التحقق من صحة المدخلات الخارجية غير الموثوقة) بينما يفرط في حماية الاستدعاءات الداخلية — حيث تتوزع المبالغة الدفاعية وفقاً لشعور النموذج العام، وليس بناءً على نموذج التهديدات الفعلي.
##أسباب المشكلة
لماذا تنتجها النماذج؟
- النفور من المخاطرة للرمز التالي / ميزة "المساعدة" في RLHF. يتم ضبط النماذج لتبدو دقيقة وتتجنب أن تكون "مخطئة". إن إصدار جملة حماية إضافية أو كتلة
try/catchيمثل مخاطرة منخفضة لهدف متنبئ الرمز التالي ويظهر على أنه عمل دقيق، لذلك يتم تكراره بكثرة. تبدو سلاسلif (array && array.length > 0)المتكررة كتحوّط ضد عدم يقين النموذج نفسه بشأن تدفق الكود. - غياب سياق تدفق البيانات على مستوى المستودع بأكمله. لا يستطيع النموذج رؤية أن المستدعي قد تحقق بالفعل من صحة المعامل، أو أن نوع TypeScript يجعل فرعاً معيناً غير قابل للوصول. وعندما لا يستطيع النموذج استنتاج تدفق البيانات خارج الملف الحالي، يلجأ افتراضياً إلى حماية كل شيء محلياً.
- المحاكاة العمياء للأوامر البرمجية الأمنية (Security-prompt cargo-culting). عندما يُطلب من النموذج "جعل الكود آمناً" دون تهديد محدد يعالجه، قد يكتفي بتغليف الكود بكتل
try/catchكما لو كانت إجراءً أمنياً بحد ذاتها. فتحل المبالغة الدفاعية محل الفهم الفعلي. - انحياز بيانات التدريب نحو كود مطول بأسلوب تعليمي يوضح كل فحص كجزء من عملية الشرح البرمجي، بالإضافة إلى رفض إعادة الهيكلة: وجدت Ox Security تجنباً لإعادة الهيكلة بنسبة 80-90% في أكواد الذكاء الاصطناعي وإفراطاً في التخصيص بنسبة 80-90% — فالنموذج يضيف فقط، ونادراً ما يزيل.
لماذا يسبب ضرراً؟
- قابليته الصيانة وعبء المراجعة. يتضمن تصنيف لأوجه عدم الكفاءة في كود بايثون المولد بنماذج اللغة الكبيرة فئة Unnecessary Conditional Block (كتلة شرطية غير ضرورية) والخطوات الزائدة أو غير الضرورية (arXiv 2503.06327) — وهذا بالضبط ما تمثله جمل الحماية الميتة. يجب على المراجعين قراءة كل فرع ميت للتأكد من أنه ميت بالفعل.
- الصحة، وليس مجرد الفوضى. تخفي الكتل الشاملة التي تبتلع الأخطاء أو تعالجها بشكل عام حالات الفشل الحقيقية؛ فكتلة
try/catchالتي تعيد قيمة افتراضية تحول الخطأ إلى سلوك خاطئ صامت. وتخلق جمل الحماية أيضاً فروعاً ميتة تجبر الاختبارات على تغطيتها، مما يؤدي إلى تضخيم نسبة تغطية الاختبارات باختبارات عديمة المعنى (وهي نتيجة أخرى توصلت إليها Ox). - مضاعفة الديون التقنية. وجد تحليل GitClear لعام 2025، الذي شمل 211 مليون سطر معدَّل (2020–2024)، أن نسبة الأسطر المرتبطة بإعادة الهيكلة (الأسطر "المنقولة") انخفضت من 25% من الأسطر المعدلة في 2021 إلى أقل من 10% في 2024، بينما ارتفعت نسبة الأسطر المنسوخة/الملصقة من 8.3% إلى 12.3% خلال الفترة نفسها؛ وكان عام 2024 أول عام تتجاوز فيه الأسطر المنسوخة/الملصقة الأسطرَ المنقولة، كما تضاعف عدد كتل الكود التي تضم خمسة أسطر مكررة أو أكثر نحو ثماني مرات خلال 2024 (GitClear 2025). والكود الدفاعي الجاهز هو بالضبط نوع الكود الذي يُستنسخ بدلاً من استخراجه، فتحتاج كل نسخة إلى قراءة واختبار ثم إصلاح بشكل منفصل.
- التعقيد الإدراكي المرتفع لكل دالة يجعل العثور على المنطق الحقيقي أصعب، مما يبطئ أي تغيير مستقبلي.
##المعالجة
تكتيكات المراجعة وصياغة الأوامر
- اجعل العقد واضحاً لتصبح جمل الحماية غير ضرورية بشكل مثبت. قل للنموذج: "
userغير قابل للقيمة null وقد تم التحقق من صحته بالفعل من قبل المستدعي — لا تعيد فحصه مجدداً". اعتمد على الأنواع: مع تشغيل خيارstrictNullChecks، ستقوم القاعدة@typescript-eslint/no-unnecessary-conditionبتحديد جمل الحماية الميتة بالنيابة عنك. - اطلب دفاعاً متناسباً، وليس دفاعاً شاملاً. وجّه النموذج: "لا تعالج سوى الأخطاء التي يمكنك وصف محفز ملموس لها. تحقق من صحة المدخلات غير الموثوقة/الخارجية عند الحدود؛ وثق بالاستدعاءات الداخلية". هذا يفصل التحقق الحقيقي من صحة المدخلات (احتفظ به) عن حالات الحافة الوهمية (احذفها).
- اطلب من النموذج تشغيل أداة الفحص/مصحح الأنواع وإزالة ما يحدده من علامات تحذيرية قبل إرجاع الكود — مما يغلق حلقة التقييم بالطريقة التي يوصي بها ممارسو الوكلاء البرمجية (استخدام أدوات الفحص، ومصححات الأنواع، والاختبارات كإشارات تقييم تلقائية).
- اطلب منه الحذف، وليس الإضافة فقط: "أعد الهيكلة للحصول على الحد الأدنى من الكود الذي يلبي المواصفات؛ وأزل الفروع غير القابلة للوصول وكتل catch التي لا يمكن أن تنطلق". هذا يواجه الانحياز الموثق لـ "تجنب إعادة الهيكلة".
إعادة الهيكلة
حدد الحركات الكلاسيكية: احذف الكود الميت، وطبّق دمج التعبيرات الشرطية، واستبدال الشرط المتداخل بعبارات الحماية، و(بالنسبة للمدخلات التي تحتاج بالفعل إلى فحص) إدخال توكيد — أو تحقق مرة واحدة عند الحدود — بدلاً من تكرار الفحص. وحيثما نُسخت جملة الحماية نفسها ولُصقت، استخدم استخراج الدالة.
// بعد — يضمن النوع عدم كونه null؛ التحقق لمرة واحدة، ولا توجد كتلة catch وهمية
function getDisplayName(user: User): string {
const name = user.profile.name.trim();
return name || "Unknown";
}
إذا كانت القيمة غير موثوقة بالفعل، فتحقق من صحتها مرة واحدة عند الحدود ودع بقية الكود يثق بالنوع الذي تم تضييقه الآن:
// الحدود: تحليل/التحقق من صحة المدخلات غير الموثوقة لمرة واحدة
const user = UserSchema.parse(rawInput); // يطلق استثناءً عند وجود بيانات سيئة، هنا، عن قصد
// ...كل ما يتبع يأخذ User متحقق من صحته ولا يحتاج إلى إعادة حماية
قاعدة ذهبية للمراجعين: يجب أن تجيب كل جملة حماية وكل كتلة catch على السؤال التالي: "ما هو المستدعي أو المدخل الملموس الذي يحفز هذا؟" إذا كانت الإجابة "لا شيء — فقط للاحتياط"، فقم بحذفها.
##أدوات الكشف
- typescript-eslint @typescript-eslint/no-unnecessary-condition — منع الشروط غير الضرورية (no-unnecessary-condition)
- ESLint no-useless-catch — منع كتل catch عديمة الفائدة (no-useless-catch)
- SonarSource (SonarQube / eslint-plugin-sonarjs) RSPEC-2589 — منع التعبيرات المجانية (no-gratuitous-expressions)
- SonarSource (SonarQube / eslint-plugin-sonarjs) RSPEC-3776 — التعقيد الإدراكي (cognitive-complexity): تزيده الفحوصات الدفاعية المتداخلة؛ كاشف غير مباشر