Back home

إخفاء التعقيد والنقل في "تحسين قابلية الصيانة"

عندما يتم نقل التعقيد ببساطة من الوظائف الكبيرة إلى التسلسل الهرمي للفئة والتكوين وسلاسل الاتصال، فإن النظام عادة لا يكون أكثر قابلية للصيانة.

عندما تقوم العديد من الفرق بـ “تحسين قابلية الصيانة”، فإن الخطوة الأولى هي تفكيك الكود.

تم تقسيم دالة مكونة من 300 سطر إلى 12 فئة؛ تم تغيير العملية التي تحتوي على العديد من الفروع إلى “الاستراتيجية + المصنع + التكوين”؛ تم تغيير المنطق الذي يمكن فهمه من خلال متابعة المكالمة إلى أحداث ومشتركين وجداول قواعد والعديد من الأدلة ذات المظهر النظيف.

في الواقع، الكود أقل ازدحامًا، والملف الواحد أقصر. عند المراجعة، فإنه يمنح الناس شعورًا بأن “هذا متقدم”.

لكن رأيي هو: ** العديد مما يسمى بتحسينات قابلية الصيانة لا تقلل من التعقيد، ولكنها تغير فقط التعقيد من مرئي جزئيًا إلى متناثر ومتقلب ومخفي. ** النتيجة الأكثر شيوعًا لهذا النوع من التغيير هي أنه من الصعب تحديد المشكلة، ومن الصعب تقييم التغيير، ومن الصعب على الأشخاص الجدد فهمه.

لقد كان جوهر قابلية الصيانة دائمًا هو: **عندما تتغير المتطلبات، وتحدث أخطاء عبر الإنترنت، وتنشأ شروط حدودية، هل يستطيع الفريق رؤية القيود الحقيقية بسرعة وإجراء تعديلات آمنة ضمن نطاق محدود؟ **

التعقيد لن يختفي بالانقسام، بل سيبقى في مكان آخر.

الموقف الشائع هو أن حدس “قابلية الصيانة” مرئي للغاية.

إنهم يشعرون بعدم الارتياح عندما يرون وظائف كبيرة، ويشعرون بالتخلف عندما يرون الكثير من “إذا/آخر”، ويريدون غريزيًا تمزيقها عندما يرون أحكامًا تجارية متعددة محشورة في الفصل الدراسي. لذلك تم تقسيم المنطق المعقد إلى العديد من الملفات الرفيعة، وتمت ترجمة الفروع الشرطية إلى مستويات كائن، وتم نقل قواعد العمل إلى التكوينات، وتمت إضافة القليل من الواجهة والتسمية، وأصبح سطح التعليمات البرمجية أكثر نظافة على الفور.

المشكلة هي أنه على الرغم من أن الأسطر الـ 300 الأصلية من التعليمات البرمجية قبيحة، إلا أن التعقيد منتشر على الأقل على سطح المكتب. من خلال القراءة من الأعلى إلى الأسفل، يمكنك رؤية كيفية ارتباط شروط الفرع والحالة المشتركة ومعالجة الاستثناءات والنتائج النهائية.

وبمجرد أن يتم حل التعقيد، يتغير الوضع:

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

لم ينخفض ​​التعقيد، بل تغير من “متعب قليلاً عند القراءة” إلى “أبطأ بكثير عند تحديد المشاكل”.

عادةً ما تتم تسوية تكلفة الصيانة بعد ثلاثة أشهر عندما يقوم شخص ما بتصحيح المنطق الخاطئ، وفشل الاتصال بالإنترنت، واستكشاف أخطاء الارتباط وإصلاحها.

سوء التقدير الأكثر شيوعًا للفرق هو الخلط بين “النظافة الجزئية” و"قابلية الصيانة الشاملة"

هذا النوع من سوء التقدير شائع لأن العديد من فوائد إعادة الهيكلة تبدو حقيقية على المدى القصير.

على سبيل المثال، يمكن تغيير الوظيفة التي تحتوي على فروع معالجة أوامر متعددة إلى البنية التالية:

Handler h = handlerFactory.get(order.type());
h.validate(order);
h.price(order);
h.persist(order);
h.notify(order);

من المؤكد أن هذا الرمز يبدو أنظف من قائمة طويلة من الفروع.

لكن السؤال الحقيقي هو:

  1. كيفية تحديد التطبيق الذي سيتم استخدامه لـ handlerFactory؛
  2. هل هناك أي متطلبات مشتركة بين validate/price/persist/notify؛
  3. ما إذا كان الانحراف السلوكي مسموحًا به بين التطبيقات المختلفة؛
  4. هل يجب إجراء تغيير متطلب معين في مكان واحد، أو أربعة أماكن، أو عشرات الأماكن؟

إذا لم تكن هذه المشكلات مقيدة، فإن هذا النوع من “البنية الأنيقة” غالبًا ما يعيد كتابة اختلافات العمل التي تمت كتابتها بشكل صريح في الأصل في if/else إلى اختلافات ضمنية منتشرة في التسلسل الهرمي للفئة.

من منظور المراجعة، يصبح الأمر أكثر نظافة؛ ومن منظور الصيانة، يصبح الأمر أكثر اعتمادًا على السياق.

** تشير قابلية الصيانة إلى ما إذا كان النظام بأكمله أسهل في الإجابة على السؤال “أين سيؤثر هذا التغيير؟” **

ما يحدد تكاليف الصيانة عادة هو أربعة أشياء

أفضل استخدام الأسئلة الأربعة التالية للحكم على ما إذا كانت إعادة الهيكلة تجعل النظام أكثر قابلية للصيانة.

1. عند حدوث المشكلة، هل مسار الموقع أقصر؟

عند الإبلاغ عن مشكلة عبر الإنترنت مفادها أن “أنواعًا معينة من الطلبات تصدر أحيانًا قسائم مكررة”، فإن الشيء الأكثر قيمة هو ما إذا كان المهندس يمكنه العثور بسرعة على: أين توجد شروط الحكم، وأين توجد الحماية العاجزة، وأين يتم تشغيل الآثار الجانبية.

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

2. عندما تتغير المتطلبات، هل يكون نطاق التعديل أكثر تقاربًا؟

التجريد الجيد يحافظ على تركيز التغييرات. التجريد السيئ يسمح للتغيير بالانتشار.

أسوأ أنواع إعادة الهيكلة هو تقسيم المنطق إلى مسؤوليات متعددة على السطح. في الواقع، في كل مرة تتغير المتطلبات، يجب تغييرها في وقت واحد: تعريف القاعدة، وتسجيل المصنع، والتكوين الافتراضي، وعينات الاختبار، ونقاط المراقبة. أصبح الملف أصغر، لكن مساحة التغيير أصبحت أكبر.

يبدو هذا النوع من الأنظمة نموذجيًا، ولكنه في الواقع أكثر هشاشة، لأنه في كل مرة تقوم فيها بإجراء تغيير، عليك أن تراهن أنك لم تفوت أي زاوية.

3. هل أصبحت القيود أكثر وضوحًا بدلًا من أن تكون مخفية؟

السبب وراء صعوبة الكثير من منطق الأعمال هو أنه يبدو ظاهريًا أن الكود قبيح، ولكنه في الواقع أقرب إليه وله العديد من المتطلبات الأساسية:

  • هذه الحالة لا يمكن أن تنتقل إلا من A إلى B، وليس مباشرة إلى C؛
  • لا يمكن تعديل هذا الحقل إلا من خلال أنواع معينة من العملاء؛
  • يجب أن يكون هذا الإجراء ناجحًا وله أثر جانبي آخر.

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

4. هل ردود الفعل في الاختبار أقرب إلى السلوك الحقيقي؟

ستؤدي العديد من “تحسينات قابلية الصيانة” بشكل ملائم إلى مجموعة من الاختبارات الفردية التي يسهل كتابتها لأن كل فئة أصغر حجمًا ويتم الاستهزاء بالتبعيات.

ومع ذلك، فإن الزيادة في عدد الاختبارات الفردية لا تعني أن النظام أسهل في التحسين.

إذا كان الاختبار قادرًا فقط على إثبات أن “هذه الفئة ستعيد القيمة المتوقعة في العالم الوهمي” ولكن لا يمكنها تغطية علاقات التجميع والحالة المشتركة وقيود التوقيت في العملية الحقيقية، فإن الأمر يتعلق بحماية البنية أكثر من حماية السلوك.

سوء فهم شائع: من أجل حذف if/else، أعد كتابة اختلافات العمل في نظام كتابة

بالطبع إذا/آخر يمكن كتابته بشكل سيئ، ولكن “حذف إذا/آخر” ليس هدفًا في حد ذاته.

لقد رأيت العديد من الأنظمة التي كانت تحتوي في الأصل على فرعين أو ثلاثة فروع واضحة ودلالات تجارية مستقرة للغاية. ومع ذلك، من أجل متابعة نمط التصميم الصحيح، تم تقسيمها إلى واجهات السياسة، والفئات الأساسية المجردة، ومراكز التسجيل، ونقاط الامتداد. وبعد مرور نصف عام، زاد عدد الأنواع من 3 إلى 9، ولكن أصبح من الصعب على نحو متزايد على المتصلين الحكم على الاختلافات التي كانت اختلافات تجارية حقيقية وأيها كانت مجرد اختلافات هيكلية متبقية من التطور التاريخي.

في كثير من الحالات، وجود العديد من الفروع لا يعني أنه يجب اعتماد نموذج الكائن؛ هذا يعني فقط أن هناك حكمًا تجاريًا هنا. أول شيء يجب فعله هو التمييز بين أي من هذه الأحكام هي محاور ثابتة للتغيير وأيها مجرد تشعبات مشروطة في نفس العملية.

إذا كان الأمر مجرد عدد قليل من الأحكام الشرطية في العملية، فمن المحتمل أن يؤدي إجبارها على أن تكون “موجهة للكائنات” إلى إعادة كتابة الشروط التي يمكن رؤيتها في لمحة سريعة في عدة طبقات من إرسال الطريقة.

**إخفاء الحالة في تعدد الأشكال لن يؤدي إلى اختفاء الحالة، بل سيجعل القارئ يدرك وجودها لاحقًا. **

سوء فهم شائع آخر: التعامل مع التكوين باعتباره سلة مهملات معقدة

هناك أسلوب آخر يسهل الخلط بينه وبين “أكثر قابلية للصيانة” وهو تكوين قواعد العمل قدر الإمكان.

عادةً ما تكون الأسباب جيدة جدًا: ليست هناك حاجة لتغيير الكود في المستقبل، والعملية قابلة للتكوين، والتوسيع أكثر مرونة.

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

بمجرد أن يبدأ تكوين القاعدة في تحمل الكثير من المسؤولية، يمكن أن تنشأ هذه المشكلات بسرعة:

  • هناك علاقة أولوية وتغطية بين التكوينات، ولكن لا يوجد مكان في النظام يمكن رؤيتها بالكامل؛
  • لا يمكن التحقق من السيناريوهات المتأثرة بالتغيير إلا عبر الإنترنت؛
  • قيم التكوين القانوني لا تعني الدلالات الصحيحة، سيتم كشف الأخطاء في وقت التشغيل؛
  • تصبح مراجعة الكود “لا أستطيع فهم ما يعنيه JSON هذا.”

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

التكلفة الشائعة للإفراط في التكوين هي أنه “لم يعد أحد يجرؤ على لمس النظام بعد الآن”.

مثال مضاد: بعض التجريدات ستجعل النظام أكثر قابلية للصيانة

ولا يمكن أن يقال “لا تجرد، لا تنقسم”.

هناك حالات لا يكون فيها التجريد جديرًا بالاهتمام فحسب، بل ضروري أيضًا.

على سبيل المثال:

  • مواجهة محاور التغيير المستقرة والواضحة، مثل واجهات التخزين الخلفية المختلفة، وقنوات الدفع المختلفة، وبروتوكولات التسلسل المختلفة؛
  • هذه التغييرات تحتاج حقًا إلى الاستبدال في وقت التشغيل، بدلاً من مجرد “ملحقات محتملة لاحقًا” وهمية؛
  • يمكن أن يتوافق كل تطبيق مع نفس مجموعة القيود القوية، بدلاً من أن يبدو أن له نفس الواجهة ولكن في الواقع له دلالات مختلفة؛
  • تتبع حدود الفريق أيضًا حدودًا مجردة، ويمكن تطوير وحدات مختلفة واختبارها بشكل مستقل.

قيمة التجريد في هذه المرحلة هي أنه يقلل بالفعل من احتكاك التغييرات المستقبلية.

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

لذا كان السؤال دائمًا: بعد الانتهاء من الهدم، يتم احتواء التعقيد، أم يتم نقله فقط إلى زوايا معرفية أخرى. **

طريقة حكم أكثر عملية: انظر أولاً إلى التعديلات الأكثر شيوعًا في المستقبل، ولا تنظر أولاً إلى نظافة الهيكل اليوم

إذا كنت أشك في أن “إعادة بناء قابلية الصيانة” هو مجرد تلميع هيكلي، فعادةً ما أبدأ بطرح ثلاثة أسئلة عملية للغاية:

  1. في المرة القادمة التي يغير فيها المنتج هذا المطلب، ما هي التغييرات المحتملة للمهندسين؟
  2. إذا حدث خطأ ما عبر الإنترنت في المرة القادمة، ما هو الرابط الذي يجب على الشخص المناوب أن ينظر إليه أولاً؟
  3. إذا تولى شخص جديد المسؤولية، فإنه يحتاج أولاً إلى فهم قواعد العمل أو هيكل الإطار.

إذا أصبحت الإجابات على هذه الأسئلة الثلاثة أكثر تعقيدًا، فهناك احتمال كبير بأن إعادة الهيكلة هذه لن تؤدي إلى تحسين قابلية الصيانة.

قابلية الصيانة مخصصة لتكاليف التعديل المستقبلية، وليس لقطات شاشة التعليمات البرمجية الحالية.

ملخص

السبب وراء خطورة العديد من “تحسينات قابلية الصيانة” هو أنها تبدو ظاهريًا وكأنها عديمة الفائدة تمامًا، ولكنها في الواقع أقرب بكثير إلى كونها من السهل جدًا أن تبدو صحيحة جزئيًا.

هناك المزيد من الفئات، وأصبحت الوظائف أقصر، وأصبح الدليل أكثر ترتيبًا، وأصبحت عملية المراجعة أكثر سلاسة. لكن تكلفة الصيانة الحقيقية تأتي من الفهم وتحديد المواقع والتعديل والتحقق، وليس من الدقة البصرية.

لذا فإن اقتراحي بسيط للغاية: ** لا تفكك المنطق المعقد حتى يصبح غير مرئي، قم أولاً بتفكيك المنطق المعقد حتى يمكن تغييره. **

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