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

تجنب إعادة الهيكلة.

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

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

يرى المراجع فرقاً يتكون بالكامل تقريباً من إضافات إلى كود موجود. لقد حل النموذج المطلوب بتكديس فرع أو علم أو طبقة إضافية فوق الدالة التي وُجّه إليها، بدلاً من إعادة تشكيل تلك الدالة ليتسع لها التغيير. العلامات الدالة:

  • فروق لا تحتوي إلا على إضافات: لا أسطر منقولة أو معاد تسميتها أو محذوفة؛ والدالة الملموسة لا تفعل سوى أن تطول.
  • معاملات أعلام: isWholesale وskipValidation وlegacy = false تُلحق بتوقيع موجود، وكل منها يبدّل السلوك في عمق جسم الدالة.
  • تضخم الفروع: كل متطلب جديد يصبح else if أو case إضافياً في الدالة نفسها بدلاً من جدول بحث أو استراتيجية أو استدعاء متعدد الأشكال.
  • غلاف فوق الأصل: تستدعي processOrderV2 الدالة processOrder وترقّع نتيجتها بدلاً من تغييرها؛ فيعيش المساران القديم والجديد جنباً إلى جنب.
  • مقاييس ترتفع في المكان نفسه: التعقيد المعرفي والطول وعدد المعاملات للدوال نفسها تزداد من إيداع إلى آخر — نقطة ساخنة تكبر.
  • أعلام ميتة لا تُزال أبداً: معاملات يمررها كل المستدعين الآن بالطريقة نفسها، وتُترك "احتياطاً".
// كل طلب أضاف معاملاً وفرعاً؛ ولم تُعد هيكلة أي شيء قط.
function priceOrder(
  items: Item[],
  discount?: number,
  isWholesale = false,
  applyVat = true,
  couponCode?: string,
) {
  let total = items.reduce((s, i) => s + i.price * i.qty, 0);
  if (discount) total = total * (1 - discount);
  if (isWholesale) {
    if (total > 1000) total = total * 0.9;
    else if (total > 500) total = total * 0.95;
  }
  if (couponCode === "SPRING") total = total - 10;
  else if (couponCode === "VIP") total = total * 0.85;
  if (applyVat) total = total * 1.2;
  return total;
}

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

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

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

  • انحياز التنبؤ بالرمز التالي نحو الإضافة. يكمل النموذج اللغوي الكبير الأمر الذي أمامه. وإلحاق فرع أو معامل عند نقطة التغيير هو التكملة الأعلى احتمالاً محلياً والأقل خطورة؛ أما إعادة تشكيل الدالة فتتطلب الإبقاء على الوحدة كاملة ومستدعيها في سياق العمل والتنبؤ بتعديل متسق على المستوى العام، وهو أصعب ولا يكافئه الأمر المباشر.
  • سياق مستودع ضئيل. نادراً ما تحمّل مساعدات البرمجة كل المستدعين وكل الاختبارات. ومن دون رؤية من يعتمد على الدالة، لا يستطيع النموذج معرفة ما إذا كانت إعادة هيكلتها آمنة، فيترك البنية كما هي ويضيف if حيث يرى. وتصف OX Security الذكاء الاصطناعي بأنه "جيش من المطورين المبتدئين الموهوبين لكن عديمي الخبرة" — منتجون محلياً، دون نظر إلى البنية.
  • إعادة الهيكلة محفوفة بالمخاطر ولا تُكافأ. تمس إعادة الهيكلة كوداً لم يُطلب من النموذج تغييره، وقد تكسر المستدعين والاختبارات. والمساعد المتملق الذي يعمل بمنطق "فقط اجعله يعمل" يقلل نطاق الضرر بعدم لمس الكود العامل — وهو بالضبط السلوك الذي قاسته OX باسم "تجنب إعادة الهيكلة" (Avoidance of Refactors) في 80–90% من الكود المولد بالذكاء الاصطناعي: فالذكاء الاصطناعي "يتفوق في توليد كود جديد لكنه لا يعود أبداً لتحسين البنية".
  • التوليد رخيص والحذف مخيف. إنتاج 40 سطراً جديداً لا يكلف النموذج شيئاً؛ أما إقناعه بحذف 40 سطراً موجوداً وإعادة تشكيلها فيعاكس غريزته في الحفاظ على ما هو قائم.

لماذا يضر

  • إعادة الهيكلة تختفي من السجل. وجد تحليل GitClear لعام 2025 لـ 211 مليون سطر معدّل أن الأسطر المرتبطة بإعادة الهيكلة ("المنقولة") انخفضت من 25% من الأسطر المعدلة في 2021 إلى أقل من 10% في 2024، بينما ارتفعت الأسطر المنسوخة والملصقة من 8.3% إلى 12.3% وتجاوزت الأسطر المنقولة لأول مرة في 2024.
  • التعقيد يتراكم. كل علم يُعلّق يضاعف التوليفات التي يجب أن تعالجها الدالة وأن تغطيها الاختبارات؛ وكل else if إضافي يجعل التغيير التالي أخطر — فيضيف الأمر التالي بدوره بدلاً من إعادة الهيكلة.
  • تراكم الدين التقني. إعادة الهيكلة هي إعادة العمل التي تبقي الفوضى منخفضة؛ وقمعها يعني إنشاء دين لا يُسدد أبداً. فتنمو قاعدة الكود أسرع مما تتحسن.
  • عبء المراجعة. يتلقى المراجعون فروقاً كلها إضافات إلى دوال طويلة أصلاً؛ والحكم على تفاعل فرع جديد مع الفروع الاثني عشر التي فوقه أصعب من مراجعة دالة أعيد تشكيلها لاستيعاب التغيير.

##المعالجة

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

  • اجعل التغيير سهلاً أولاً. نصيحة كنت بيك — "لكل تغيير مطلوب، اجعل التغيير سهلاً (تحذير: قد يكون هذا صعباً)، ثم أجرِ التغيير السهل" — تتحول مباشرة إلى أمر: "قبل إضافة قاعدة القسيمة، أعد هيكلة priceOrder بحيث تصبح القاعدة الجديدة إضافة من سطر واحد؛ حافظ على السلوك نفسه وشغّل الاختبارات. ثم أضف الميزة."
  • امنع معاملات الأعلام الجديدة. "لا تضف معاملات منطقية أو معاملات أوضاع إلى دوال موجودة؛ وإذا كان يجب أن يختلف السلوك، فاقترح إعادة هيكلة أولاً."
  • ضع ميزانية للتعقيد لا للصحة وحدها. اطلب من المساعد تشغيل قواعد ESLint ‏complexity وmax-params وmax-lines-per-function وقاعدة التعقيد المعرفي في Sonar، والإبلاغ عن القيم قبل التغيير وبعده لكل دالة لمسها؛ وأي زيادة تحتاج إلى تبرير.
  • اطلب إعادة الهيكلة خطوةً منفصلة. التوليد وإعادة الهيكلة مهمتان مختلفتان: أولاً "اجعله يعمل"، ثم تغيير منفصل يحافظ على السلوك — "الآن أعد هيكلة هذه الدالة بحيث تصبح الفروع بيانات أو دوالاً منفصلة" — مع اختبارات ناجحة قبل التغيير وبعده.
  • راقب النقاط الساخنة. تتبّع معدل التغيّر × التعقيد (عبر Code Maat أو سجل git ببساطة) للملفات التي تكبر مع كل إيداع يكتبه الذكاء الاصطناعي، وجدول إعادة هيكلتها قبل أن تصل إليها الميزة التالية.

إعادة الهيكلة الفعلية — تفكيك التعبيرات الشرطية واستخراج الدالة يعطيان كل فرع اسماً؛ واستبدال المعامل بدوال صريحة أو إدخال كائن المعاملات (Introduce Parameter Object) يحلان محل معاملات الأعلام؛ واستبدال الجمل الشرطية بتعدد الأشكال (Replace Conditional with Polymorphism) (أو جدول بحث) يتولى الأمر عندما تختار الفروع السلوك بحسب نوع أو رمز.

// بعد: كل قاعدة دالة صغيرة؛ و priceOrder تكتفي بتركيبها.
type PriceRule = (total: number) => number;

const subtotalOf = (items: Item[]) =>
  items.reduce((s, i) => s + i.price * i.qty, 0);

const VAT_RATE = 1.2;
const discount = (rate: number): PriceRule => (t) => t * (1 - rate);
const wholesale: PriceRule = (t) => (t > 1000 ? t * 0.9 : t > 500 ? t * 0.95 : t);
const COUPONS: Record<string, PriceRule> = {
  SPRING: (t) => t - 10,
  VIP: (t) => t * 0.85,
};
const vat: PriceRule = (t) => t * VAT_RATE;

function priceOrder(items: Item[], rules: PriceRule[]): number {
  return rules.reduce((total, rule) => rule(total), subtotalOf(items));
}

// يحدد المستدعي القواعد المطبقة وترتيبها:
// priceOrder(items, [discount(0.1), wholesale, COUPONS.SPRING, vat]);

أصبحت قاعدة التسعير الجديدة الآن مدخلاً إضافياً، لا معاملاً وفرعاً إضافيين، ويمكن اختبار كل قاعدة على حدة.

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

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

  • SonarQube / SonarSource S3776 — يجب ألا يكون التعقيد المعرفي للدوال مرتفعاً جداً — يرصد الدوال التي تواصل استيعاب فروع وأعلام جديدة
  • ESLint complexity — حد التعقيد الدوري لكل دالة (الخيار max)
  • ESLint max-lines-per-function — حد طول الدالة — يلتقط الدوال التي لا تفعل سوى النمو
  • ESLint max-params — حد عدد المعاملات — يلتقط الأعلام والخيارات المعلّقة على توقيع موجود
  • ESLint max-depth — حد عمق تداخل الكتل — يلتقط سلالم if/else المكدسة داخل فروع قديمة
  • Code Maat entity-churn — معدل تغيّر كل ملف من سجل git — مع التعقيد يكشف النقاط الساخنة التي تنمو من إيداع إلى آخر