انحراف الاصطلاحات.
الكود البرمجي المولد بالذكاء الاصطناعي الذي يتجاهل بصمت الاصطلاحات المتبعة في المستودع — من خلال إعادة اختراع الدوال المساعدة، واختيار المكتبة الخاطئة، واستخدام تسمية ومعالجة أخطاء خارجة عن الأسلوب المتبع — مما يودي بقاعدة الكود إلى الانحراف نحو أسلوب عام يمثل متوسط الإنترنت.
##Signs and Symptoms
يتعرف المراجع على انحراف الاصطلاحات عندما يعمل تعديل الذكاء الاصطناعي بنجاح ولكنه لا يبدو وكأنه ينتمي إلى قاعدة الكود. حيث يلجأ إلى نمط المتوسط العالمي بدلاً من النمط المحلي:
- كتابة دالة مساعدة جديدة بشكل مدمج (inline) على الرغم من وجود أداة مساعدة مجربة ومختبرة بالفعل (مثل
utils/date.ts،lib/apiClient، أو نوعResultمشترك). - ظهور مكتبة أو واجهة برمجة تطبيقات (API) مختلفة عن المعيار المعتمد في المستودع (مثل استخدام
axiosفي تطبيق تم توحيده على مغلفfetch؛ أو استخدامmomentحيث يستخدم المستودعdate-fns). - انحراف التسمية، وتخطيط الملفات، وأسلوب معالجة الأخطاء — استخدام
camelCaseفي وحدة مبنية بأسلوبsnake_case، أو استخدامthrow new Error("...")خام حيث يعيد كل شيء آخر خطأً محدد النوع، أو تسجيل أخطاء مخصص بـtry/catchبدلاً من المسجل المشترك. - كل متطلب جديد يحصل على كتلته البرمجية الخاصة المصممة خصيصاً له بدلاً من إعادة استخدام نموذج مجرد (ما تسميه Ox Security "الإفراط في التخصيص"، والذي يظهر في 80-90% من أكواد الذكاء الاصطناعي).
- علامة مميزة: تتضاعف الكتل المكررة. رصدت GitClear قفزة بمقدار 8 أضعاف في كتل النسخ المكررة المكونة من 5 سطور أو أكثر في عام 2024، مع تجاوز السطور المنسوخة/الملصقة للسطور المنقولة (المعاد هيكلتها) لأول مرة.
// الأسلوب المتبع (موجود بالفعل في المستودع)
import { apiClient } from "@/lib/apiClient"; // يغلف المصادقة وإعادة المحاولة ورابط URL الأساسي
import { formatDate } from "@/utils/date"; // على مستوى التطبيق، ويدعم اللغات والمنطقة
// التعديل المولد بالذكاء الاصطناعي — ينحرف عن كليهما
import axios from "axios"; // ليس نمط التبعية المتبع في المشروع
async function getUser(id: string) {
try {
const res = await axios.get(`https://api.example.com/users/${id}`); // رابط URL أساسي مكتوب بشكل مباشر
return { ...res.data, joined: new Date(res.data.joined).toLocaleDateString() }; // يعيد اختراع formatDate
} catch (e) {
console.log("error", e); // ليس المسجل المشترك؛ ويقوم ببلع (تجاهل) الخطأ
}
}
العلامة الفاضحة هي التناسق، وليس الصحة: حيث يجد ثلاثة مراجعين ثلاثة خيارات مختلفة "خاطئة ولكنها تعمل"، ولا يتطابق أي منها مع الملف المجاور.
##Reasons for the Problem
لماذا تنتجها النماذج؟
- الارتداد إلى متوسط التدريب. يتم تدريب نماذج اللغات الكبيرة على مجموعة بيانات ضخمة تمثل متوسط كود الإنترنت، وليس مستودعك الخاص. عندما لا يتم توجيهها، فإنها تطلق الأسلوب الأكثر شيوعاً إحصائياً (مثل
axios،moment،console.log)، وليس الأسلوب المتبع لديك — إذ تُعتبر اصطلاحاتك مجرد إشارة ضئيلة خارجة عن التوزيع مقارنة بالمتوسط العالمي. - الاتساق الذاتي للرمز التالي على حساب نطاق المستودع. من الأسهل محلياً إكمال كتلة قائمة بذاتها (مثل كتابة منسق تاريخ مدمج) بدلاً من "معرفة" وجود
@/utils/dateواستيراد معرف لم يره النموذج قط. تتطلب إعادة الاستخدام سياقاً للمستودع لا يمتلكه النموذج؛ بينما تتطلب إعادة الاختراع الذاكرة المؤقتة الحالية فقط. - سياق مستودع محدود أو مفقود. عادة ما تكون الدالة المساعدة المعتمدة، وإعدادات أداة الفحص (lint)، ووثائق ADR التي تنص على "استخدام مغلف fetch" خارج نص الأمر الموجه (prompt). لا يمكن للنموذج اتباع اصطلاح لم يُعرض عليه قط. كما قال إينو رييس من Factory، فإن جزءاً كبيراً من الاصطلاحات يكون ضمنياً — أنماط يمتصها البشر من خلال قراءة قاعدة الكود بينما لا يراها الوكلاء ببساطة.
- التملق / التركيز الحرفي على المهمة. عندما يُطلب من النموذج "إضافة دالة
getUser"، فإنه يفعل ذلك بالضبط ولن يتطوع ليقول "في الواقع لدينا عميل برمجي (client) بالفعل لهذا الغرض". فهو يبحث عن إكمال المهمة المذكورة، وليس ملاءمة النظام. إن "الهوس بالحلول المألوفة" الذي أشارت إليه Ox Security (في 80-90% من العينات) هو نفس القوة: حيث يتبع اصطلاحاً تقليدياً عاماً بدلاً من تقييم اصطلاحات المشروع الخاصة. - تاريخ انتهاء التدريب (Training-cutoff staleness). إذا هاجر المستودع إلى مكتبة أو نمط أحدث بعد تاريخ انتهاء تدريب النموذج، فإن النموذج يعيد بثقة تقديم الإصدار الذي تدرب عليه.
لماذا تسبب ضرراً؟
- قابلية الصيانة والعبء الإدراكي. كل خيار منحرف هو طريقة إضافية للقيام بالشيء نفسه. يجب على القراء الاحتفاظ بنماذج متعددة لـ "كيفية تنسيق التواريخ" في رؤوسهم؛ مما يفقد قاعدة الكود مصدر الحقيقة الوحيد.
- الصحة عند التعديل. ينحرف المنطق المكرر/المعاد اختراعه بصمت — فالخطأ الذي يتم إصلاحه في المساعد المشترك لا يتم إصلاحه في نسخة الذكاء الاصطناعي. يربط تقرير GitClear انفجار النسخ المكررة مباشرة بمساعدي الذكاء الاصطناعي الذين جعلوا إدراج الأكواد بضغطة زر أرخص من إعادة الاستخدام، بينما انخفضت حصة إعادة الهيكلة من التغييرات من 25% (2021) إلى أقل من 10% (2024).
- الأمان. تجاوز عمليات التحقق أو المصادقة أو بناء الاستعلامات المعاد اختراعها المسار المشترك والمحصن (رابط URL أساسي مكتوب مباشرة، أخطاء متجاهلة، استعلامات SQL نصية عشوائية). تسمي Ox التأثير التراكمي بـ "جيش من المطورين المبتدئين": سريع وعملي، ولكن دون حكم معماري.
- عبء المراجعة وتراكم الديون التقنية. لا يتم التقاط الانحراف بواسطة الاختبارات (لأن الكود يعمل)، لذلك يستقر في مرحلة المراجعة أو لا يُكتشف على الإطلاق، متراكماً ليتحول إلى رائحة "العمليات المجزأة / سراب الوحدات (Scattered Functionality / Modular Mirage)" المعمارية حيث يتفتت السلوك المترابط عبر ملفات متعددة دون أي تماسك حقيقي.
##Treatment
تكتيكات صياغة الأوامر وسير العمل
- اعرض الاصطلاحات. ضع القواعد حيث يقرأها الوكيل البرمجي — مثل ملف
CLAUDE.md/AGENTS.md/ملف القواعد الذي يسرد العميل البرمجي المعتمد، والمسجل، ونوع الخطأ، والتسمية، مع توجيه "استخدم X وليس Y". ما لا يستطيع النموذج رؤيته، لا يمكنه اتباعه. - أشر إلى الكود المرجعي. "استخدم
@/lib/apiClientو@/utils/date؛ لا تضف مكتبات HTTP جديدة. طابق معالجة الأخطاء فيservices/orders.ts". قدم أمثلة قليلة لأسلوب المستودع عن طريق لصق ملف نموذج واحد. - اسأل قبل أن يكتب. "ما هي الدوال المساعدة/النماذج المجردة الحالية التي تغطي هذا؟ أعد استخدامها؛ ولا تضف كوداً جديداً إلا إذا لم يكن أي منها مناسباً". هذا يحول إعادة الاختراع إلى إعادة استخدام مسبقة.
- اجعل أداة الفحص هي الحارس. اطلب من الوكيل البرمجي تشغيل المنسق (formatter)، وأداة الفحص (linter)، وفاحص الأنواع (type-checker) وإصلاح جميع النتائج قبل تقديم الكود. نصيحة Factory هي ميكنة إشارات الجودة (الفحص/التنسيق/فحص الأنواع/الاختبار) بحيث يعمل الوكيل البرمجي على تحسين الكود نحوها بدلاً من الانحراف.
- راجع من أجل الملاءمة، وليس الوظيفة فقط. افحص التغييرات للبحث عن استيراد مكتبات غير معتمدة؛ وابحث عن المنطق الذي كان ينبغي أن يستدعي الدالة المساعدة المشتركة؛ وشغّل مكتشف النسخ واللصق على طلب السحب (PR).
إعادة الهيكلة — حدد الحركات الكلاسيكية: إزالة التكرار (Remove Duplication) / استبدال الكود المدمج باستدعاء دالة (Replace Inline Code with Function Call)، واستبدال الخوارزمية (Substitute Algorithm)، واستخراج الدالة (Extract Function) إذا لم يكن النموذج المجرد المناسب موجوداً بعد.
// قبل — منحرف، يعيد اختراع منطق العميل والتاريخ، رابط URL مكتوب مباشرة، خطأ متجاهل
import axios from "axios";
async function getUser(id: string) {
try {
const res = await axios.get(`https://api.example.com/users/${id}`);
return { ...res.data, joined: new Date(res.data.joined).toLocaleDateString() };
} catch (e) {
console.log("error", e);
}
}
// بعد — يعيد استخدام الاصطلاحات المعتمدة
import { apiClient } from "@/lib/apiClient";
import { formatDate } from "@/utils/date";
async function getUser(id: string) {
const user = await apiClient.get<User>(`/users/${id}`); // رابط URL الأساسي، المصادقة، إعادة المحاولة، ومعالجة الأخطاء محددة النوع هنا
return { ...user, joined: formatDate(user.joined) };
}
إذا ظهرت نفس الكتلة المنحرفة في عدة طلبات سحب (PRs)، فهذه إشارة إلى أن الاصطلاح غير قابل للاكتشاف — أصلح السبب الجذري بتوثيقه في ملف القواعد و/أو كشفه من خلال نقطة إدخال واحدة واضحة، بدلاً من إعادة مراجعة كل حالة على حدة.
##Detected by
- jscpd duplication threshold (min-lines / min-tokens) — كشف النسخ/اللصق
- PMD CPD CPD duplicated code blocks — مكتشف النسخ واللصق
- ESLint no-restricted-imports — no-restricted-imports
- typescript-eslint @typescript-eslint/naming-convention — naming-convention
- SonarQube Source files should not have any duplicated blocks — الكتل المكررة