لدي اعتراف. في عام 2023، أمضيت ثلاثة أسابيع في بناء نظام إضافات plugin system لإطار عمل أتمتة الاختبارات. مشغّلات اختبار قابلة للتكوين. إضافات قابلة لإعادة التحميل السريع. حاوية حقن تبعيات. كل شيء.
لم يكتب أحد أي إضافة على الإطلاق.
كان الإطار يعمل في CI بنفس التكوين في كل مرة. "قابلية التوسع" التي بنيتها لم يستخدمها أي شخص. كان بإمكاني تسليم المشروع بأكمله في 4 أيام دون بنية الإضافات.
كيف يحدث الإفراط في الهندسة
يبدأ بفكرة معقولة: "ماذا لو احتجنا لتوسيع هذا لاحقًا؟"
تلك الفكرة هي الفخ. لأن "لاحقًا" نادرًا ما يبدو كما تخيلت، والتجريدات abstractions التي تبنيها لمتطلبات وهمية عادةً ما تعيق المتطلبات الحقيقية.
إليك التطور الذي لاحظته في نفسي:
- بناء دالة بسيطة ✅
- التفكير "يجب أن تكون هذه قابلة للتكوين" ⚠️
- إضافة كائن تكوين config object
- التفكير "قد تحتاج البيئات المختلفة إلى تطبيقات مختلفة" ⚠️
- إضافة واجهة interface ونمط المصنع factory pattern
- التفكير "قد نحتاج لتبديل هذا أثناء التشغيل" 🚩
- إضافة حقن التبعيات dependency injection
- إدراك أن لا أحد احتاج أبدًا لتبديله
- الحفاظ على التجريد إلى الأبد لأن إزالته أصعب من الاحتفاظ به
الأسئلة الثلاثة
قبل إضافة أي تجريد abstraction، أسأل الآن:
1. "هل طلب أحد هذا فعليًا؟"
إذا كانت الإجابة "لا، لكنهم قد يطلبون" — لا تبنِه. YAGNI (لن تحتاجه) هو المبدأ الأكثر انتهاكًا في الهندسة.
2. "ما هي تكلفة إضافته لاحقًا مقابل الآن؟"
إذا كان بإمكاني إضافة التجريد في ساعتين عندما يكون مطلوبًا فعليًا، فلا يوجد سبب لبنائه الآن "فقط في حالة." تكلفة التجريد المبكر (صيانة كود لا يستخدمه أحد) دائمًا ما تكون أعلى من تكلفة إضافته لاحقًا.
3. "هل يمكنني شرح سبب وجود هذا لشخص ما في جملة واحدة؟"
"نستخدم حقن التبعيات لأننا نحتاج لتبديل مزود الدفع بين Stripe و Braintree في بيئات مختلفة." هذا سبب حقيقي.
"نستخدم حقن التبعيات لأنها أفضل ممارسة." هذا ليس سببًا. هذا تقليد أعمى cargo culting.
كيف يبدو الكود البسيط
\\
