لماذا يصبح تعديل المشروع البرمجي الصغير أصعب مع كل ميزة؟
عالم البرمجة
يبدأ المشروع بملف أو ملفين، وهذا طبيعي.
تضيف تسجيل الدخول، ثم حفظ البيانات، ثم إرسال بريد عند اكتمال الطلب.
تعمل النسخة الأولى، ويصل المنتج إلى المستخدمين،
ولا يبدو أن هناك سببا لبناء هيكل أكبر من الحاجة الحالية.
بعد أسابيع تأتي ميزة صغيرة: إرسال إشعار جديد عند فشل الدفع.
يفتح المطور الملف الأقرب، يضيف شرطا واستدعاء للخدمة، ثم يختبر الشاشة يدويا.
تعمل الميزة، لكن التعديل التالي يكشف أن قاعدة الإشعار موجودة أيضا في مهمة خلفية،
ومسار آخر للطلبات، واختبار قديم لا يغطي الحالة الجديدة.
المشكلة لا تبدأ عندما يطول الملف؛ تبدأ عندما لا تعرف أين يجب أن تعيش القاعدة الجديدة،
وما الذي قد ينكسر إذا وضعتها في المكان الخطأ.
البداية البسيطة ليست خطأ.
الخطأ أن تتحول القرارات المؤقتة إلى روابط دائمة بين أجزاء لم يكن يفترض أن تعرف تفاصيل بعضها.
عندها يظل التطبيق صغيرا في عدد الشاشات، بينما تصبح كلفة كل تغيير أكبر من حجم الميزة نفسها.
عندما يصبح التعديل البسيط مخاطرة
الكود الصعب لا يعلن عن نفسه بعدد محدد من الأسطر.
قد يكون لديك ملف طويل لكنه واضح، ومسؤولياته مفهومة، واختباره سهل.
وقد يكون لديك مشروع موزع على عشرات الملفات، لكن قاعدة عمل واحدة مكررة بينها،
ولا يعرف أحد أي نسخة هي الصحيحة.
تظهر المشكلة عندما تحتاج إلى تغيير سلوك بسيط فتضطر إلى البحث في المشروع كله.
تغير شرطا في مسار الطلب، ثم تكتشف أن تطبيق الهاتف يعيد تنفيذ الشرط،
وأن مهمة مجدولة تعتمد على صيغة قديمة، وأن مزود البريد يستقبل بيانات صيغت داخل طبقة العرض.
هذه ليست مشكلة حجم فقط، بل مشكلة اعتماد غير ظاهر.
كل جزء يعرف أكثر مما يحتاج، وكل تعديل يحمل معه احتمالات جانبية لا تظهر في المهمة الأصلية.
من العلامات الواضحة أيضا أن الفريق يؤجل تعديلات مطلوبة لأنه يخشى لمس منطقة معينة، أو أن إصلاح الخطأ يعود بعد مدة بصورة مختلفة، أو أن نشر ميزة صغيرة يحتاج إلى قائمة طويلة
من الفحص اليدوي لا يعرفها إلا مطور واحد.
عند هذه النقطة لا يصبح السؤال: هل نحتاج إلى معمارية جديدة؟ بل: ما العلاقة التي جعلت التغيير ينتشر، وكيف نجعلها أوضح بأقل تعديل ممكن؟
وتظهر علامة إضافية عندما تصبح المعرفة محصورة في شخص واحد.
إذا غاب المطور الذي كتب المسار، يتوقف الفريق أو يبدأ التخمين.
هذه ليست مشكلة توثيق فقط؛ قد تعني أن الحدود غير واضحة، وأن أسماء الوحدات لا تكشف مسؤولياتها، وأن القرار موزع بين تفاصيل كثيرة.
الكود القابل للصيانة يسمح لمطور آخر بتتبع الفكرة من نقطة الدخول إلى النتيجة
من دون الاعتماد على ذاكرة صاحبه.
الكود يعمل لكن كلفة التغيير ترتفع
نجاح الميزة عند إطلاقها لا يكشف جودة البنية كاملة.
المستخدم يرى النتيجة، أما الفريق فيرى الوقت الذي استغرقه العثور على المكان الصحيح، وعدد الملفات التي تغيرت، والأخطاء التي ظهرت بعد الدمج، والخوف من أن تتأثر وظيفة بعيدة.
تزداد كلفة التغيير عندما تتوزع معرفة الميزة الواحدة بين مواضع كثيرة.
قاعدة مثل «لا ترسل الطلب قبل تأكيد الدفع» قد توجد في الواجهة، ومسار الخادم، ومهمة خلفية، وتطبيق منفصل.
إذا تغيرت القاعدة، يجب تذكر كل هذه المواضع وتعديلها بالطريقة نفسها.
وقد ترتفع الكلفة بسبب خدمة خارجية تسربت تفاصيلها إلى منطق العمل.
إذا كانت أسماء حقول مزود الدفع منتشرة في وحدات الطلبات والتقارير والإشعارات،
فإن استبدال المزود لا يعود مهمة محصورة، بل يصبح تغييرا واسعا يحمل مخاطر أكبر.
كذلك تزيد الحالة المشتركة القابلة للتعديل من صعوبة الفهم.
عندما تستطيع دوال كثيرة تغيير المتغير نفسه، يصبح ترتيب التنفيذ جزءا خفيا من السلوك.
يظهر الخطأ في بيئة معينة، ثم يختفي عند محاولة إعادة إنتاجه لأن مسار البيانات غير واضح.
إذن الكود لا يصبح صعبا لأن المشروع كبر فقط؛ يصبح صعبا عندما تتوزع معرفة الميزة الواحدة
بين أماكن لا تعرف أنها تعتمد على بعضها.
بوصلة التغيير الأربع
يمكن تقييم قابلية التعديل من خلال أربعة أسئلة: الموضع، والانتشار، والأمان، والرجوع.
هذه البوصلة أكثر فائدة من الحكم على المشروع بعدد الطبقات أو الملفات.
الموضع: هل تعرف المكان الطبيعي الذي تعيش فيه قاعدة العمل؟
إذا كانت قاعدة تأكيد الطلب موزعة بين الواجهة والخادم والمهمة الخلفية،
فهي لا تملك موضعا واضحا.
المطلوب ليس جمع كل شيء في ملف واحد، بل وجود مصدر معروف للقرار،
وتبعية واضحة للأجزاء التي تستخدمه.
الانتشار: كم جزءا يجب أن يتغير لتنفيذ تعديل واحد؟ ليس كل تعديل متعدد الملفات سيئا؛ فقد تتغير الواجهة والاختبار والتوثيق بصورة منطقية.
الخطر يظهر عندما ينتقل التغيير إلى ملفات لا ترتبط بالميزة بحدود مفهومة،
أو عندما تتكرر القاعدة نفسها بصيغ مختلفة.
الأمان: كيف ستعرف أن السلوك القديم لم ينكسر؟ قد يكون الأمان اختبار وحدة لقاعدة العمل، أو اختبار تكامل عند قاعدة البيانات، أو اختبارا لمسار حرج، أو مراجعة كود، أو مراقبة بعد النشر.
الاختبارات عنصر مهم، لكنها ليست العنصر الوحيد.
الرجوع: هل تستطيع عزل التغيير أو التراجع عنه إذا فشل؟ التغيير الصغير، وعلم الميزة، والنشر التدريجي، وفصل ترحيل البيانات عن تشغيل السلوك الجديد، كلها تقلل حجم الضرر المحتمل.
قبل إضافة أي ميزة، اسأل: أين تعيش القاعدة؟ كم مكانا سيحتاج إلى معرفتها؟ ما الذي سيكشف كسرها؟ وكيف نعود إلى الوضع السابق إذا لم تعمل؟ إذا غابت الإجابات،
فلا تقفز مباشرة إلى إعادة تصميم المشروع.
قد يكفي نقل قاعدة واحدة، أو استخراج وظيفة، أو إضافة اختبار حول مسار حساس.
التكرار ليس المشكلة الوحيدة
نسخ الكود ولصقه قد يكون علامة على مشكلة، لكنه ليس حكما آليا.
أحيانا يكون تكرار بسيط ومؤقت أوضح من تجريد مبكر يجمع حالتين متشابهتين
في الشكل ومختلفتين في المعنى.
يصبح التكرار خطرا عندما يمثل قاعدة عمل واحدة يجب أن تبقى متطابقة.
إذا كانت طريقة حساب الخصم موجودة في ثلاثة مواضع، فإن تعديل النسبة أو شروط الاستحقاق قد يترك نسخة قديمة تعمل بصمت.
هنا يكون توحيد القاعدة ذا قيمة واضحة.
اقرأ ايضا : لماذا تشاهد دروس البرمجة ولا تستطيع كتابة الكود وحدك؟
أما إذا كانت قطعتان من الكود متشابهتين اليوم لكنهما تخدمان سياقين مختلفين، فقد يؤدي جمعهما في دالة عامة إلى سلسلة من المعاملات والشروط والاستثناءات.
يقل عدد الأسطر، لكن يزيد عدد الحالات التي يجب فهمها قبل أي تعديل.
لهذا لا تستخرج تجريدا لمجرد أنك رأيت تشابها مرة واحدة.
ابحث عن معنى مشترك واستقرار نسبي في السلوك.
اسأل هل التغيير في حالة يجب أن ينعكس في الأخرى دائما.
إذا كانت الإجابة غير واضحة، فقد يكون إبقاء التكرار المؤقت أقل خطرا.
المشكلة الأخرى هي تعدد المسؤوليات داخل الوحدة نفسها.
ملف المستخدمين الذي يدير الجلسات والصلاحيات والدفع والبريد لا يحتاج بالضرورة إلى خمس طبقات جديدة، لكنه يحتاج إلى حدود أوضح.
استخراج الجزء الذي يتغير لسبب مختلف يجعل بقية الملف أسهل في الفهم والاختبار.
حدود بسيطة أفضل من معمارية ثقيلة
المشروع الصغير لا يحتاج تلقائيا إلى Clean Architecture كاملة، ولا إلى واجهة لكل فئة، ولا إلى خدمة مستقلة لكل وظيفة.
الهندسة المفرطة تضيف أسماء وملفات وتحويلات ذهنية قد تكون أثقل من المشكلة الأصلية.
الحد الجيد يظهر عند تفصيل متغير أو مكلف أو يصعب اختباره.
بوابة الدفع مثال مناسب: منطق الطلب يجب أن يعرف أن الدفع نجح أو فشل، لكنه لا يحتاج إلى معرفة أسماء حقول المزود أو طريقة توقيع طلباته.
محول صغير يمكنه احتواء هذه التفاصيل ومنعها من الانتشار.
الأمر نفسه ينطبق على البريد أو التخزين أو خدمة الرسائل.
لا تنشئ واجهة لأن الكتب تقول ذلك، بل لأنك تريد منع تفاصيل خارجية من الوصول إلى منطق العمل،
أو لأن الاختبار يحتاج إلى بديل يمكن التحكم فيه، أو لأن الخدمة تستخدم في مواضع متعددة.
في المقابل، لا تفصل دالة بسيطة إلى سلسلة من الوحدات لمجرد الوصول إلى شكل معماري مثالي.
كل طبقة تضيف تكلفة في القراءة والتتبع.
الهدف أن تكون الحدود قليلة وواضحة، وأن تتوافق مع أسباب التغيير الحقيقية.
التصميم الجيد لا يتنبأ بكل مستقبل محتمل.
هو يحمي الأجزاء التي ظهرت حولها إشارات فعلية: تغير متكرر، واعتماد خارجي، وقاعدة حساسة، وخطأ يصعب اكتشافه، أو عمل متزامن بين أكثر من مطور.
الاختبارات تقلل الخوف ولا تضمن السلامة
غياب الاختبارات يجعل الفريق يعتمد على الذاكرة والفحص اليدوي.
وعندما تكون آثار التغيير غير واضحة، يبدأ المطور بإضافة حلول فوق الحلول القديمة
لأنه يخشى إعادة تنظيم الأساس.
لكن الاختبارات لا تمنح ضمانا كاملا.
هي تبني ثقة في السلوك الذي تغطيه، وقد تفشل في اكتشاف حالة لم يكتب لها اختبار، أو تكامل لا يشبه بيئة الإنتاج، أو خطأ في فهم المتطلب نفسه.
ابدأ بالمسارات ذات القيمة والمخاطرة الأعلى.
اختبر قواعد الأعمال التي يجب ألا تتغير من دون قصد، والأخطاء التي تكررت، والحدود مع قاعدة البيانات
أو مزود الدفع، والمسارات التي يعتمد عليها المستخدم في إكمال عمله.
لا تجعل نسبة التغطية هدفا منفصلا عن الفائدة.
اختبار يثبت تفاصيل داخلية قابلة للتغيير قد يصبح عبئا ويعطل إعادة الهيكلة من دون حماية سلوك مهم.
الأفضل أن يركز الاختبار على النتيجة التي يراها المستدعي أو المستخدم.
المراجعة البرمجية تضيف طبقة أمان مختلفة.
يستطيع مراجع آخر أن يسأل لماذا وضعت القاعدة هنا، وهل هناك موضع آخر ينفذها، وهل التغيير أصغر مما يجب أو أكبر مما يلزم.
الهدف ليس الوصول إلى كود مثالي، بل منع قاعدة الكود من التراجع مع كل تسليم.
وعند التغييرات الحساسة، اجمع الاختبار مع مراقبة بعد النشر وخطة رجوع.
قد تنجح الاختبارات كلها وتظهر مشكلة بسبب بيانات حقيقية أو حجم استخدام لم يكن موجودا في بيئة التطوير.
أصلح المسار الأكثر تغيرا أولا
عندما يصبح المشروع صعب التعديل، تبدو إعادة الكتابة من الصفر مغرية.
المشروع الجديد سيكون أنظف، ولن يحمل القرارات القديمة.
لكن النظام الحالي يحتوي معرفة تراكمت في الحالات الاستثنائية والتكاملات وسلوك المستخدمين،
وقد تضيع هذه المعرفة أثناء النقل.
ابدأ بالمسار الذي يجمع أعلى تكرار للتغيير وأعلى تكلفة للأخطاء.
قد يكون الدفع، أو تسجيل الدخول، أو إنشاء الطلب.
لا تبدأ بأكثر ملف قبحا إذا كان مستقرا ولا يسبب أعطالا؛ ابدأ بالجزء
الذي يستهلك وقت الفريق ويمنع الميزات.
احم السلوك الحالي باختبارات مناسبة أو قائمة تحقق موثقة، ثم نفذ تغييرا صغيرا.
انقل قاعدة واحدة إلى موضع واضح، أو ضع حدا حول خدمة خارجية، أو أزل حالة مشتركة غير ضرورية.
انشر التغيير وتحقق من أثره قبل الانتقال إلى الخطوة التالية.
إعادة الهيكلة تعني تحسين بنية الكود مع الحفاظ على سلوكه الخارجي قدر الإمكان.
لذلك يجب ألا تختلط في دفعة واحدة مع تغيير واسع في المتطلبات وترحيل بيانات وإعادة تصميم كامل.
كلما صغرت الخطوات، أصبح فهم الخطأ والرجوع عنه أسهل.
سجل أيضا كلفة التغيير: كم استغرق العثور على الموضع؟ كم جزءا تغير؟ ما العطل الجانبي الذي ظهر؟
وما الاختبار الذي كان مفقودا؟ بعد عدة ميزات ستظهر مناطق تستنزف الفريق باستمرار،
وهذه أولى بالإصلاح من مناطق تبدو غير جميلة فقط.
كيف تمنع الدين التقني من العودة؟
الدين التقني ليس كل كود غير مثالي.
هو قرار يمنحك سرعة الآن مقابل تكلفة متوقعة لاحقا.
قد يكون القرار مقبولا إذا كان واعيا، محدودا، ومسجلا، وله سبب واضح.
يصبح مشكلة عندما تتراكم القرارات المؤقتة ولا يعود أحد يعرف أنها مؤقتة.
ضع قاعدة بسيطة: كل حل عاجل يهدد القابلية للتعديل يجب أن يترك أثرا مرئيا.
اكتب ملاحظة تشرح السبب والمخاطرة والشرط الذي يستدعي تحسينه.
لا تملأ قائمة لا تنتهي بمهام عامة مثل «تنظيف الكود»، بل اربط المهمة بمسار وأثر واضحين.
خصص جزءا صغيرا من العمل للتحسين أثناء تطوير الميزات نفسها.
عندما تلمس مسارا متعبا، حسنه بالقدر الذي يجعل التغيير الحالي أكثر أمانا.
لا تحاول إصلاح المشروع كله، ولا تتركه كما وجدته إذا كان تعديل محدود سيمنع تكرار المشكلة.
حافظ على تغييرات صغيرة ومراجعات سريعة وملكية واضحة للمكونات الحساسة.
اقرأ ايضا : لماذا تشاهد دروس البرمجة ولا تستطيع كتابة كود وحدك؟
وثق سبب القرارات التي لا يمكن استنتاجها من الكود، وطريقة تشغيل الاختبارات، وحدود الخدمات الخارجية، وخطة الرجوع في المسارات الحرجة.
اختر في الميزة القادمة مسارا واحدا، وطبق عليه بوصلة الموضع والانتشار والأمان والرجوع قبل كتابة الحل.
هذا هو الاختبار الحقيقي لجودة البنية، لا عدد الأنماط المعمارية الموجودة في المستودع.
المشروع القابل للصيانة ليس المشروع الذي لا يتعقد أبدا؛ بل المشروع الذي يكشف كلفة التغيير مبكرا، ويمنح الفريق طريقة محددة لتقليلها قبل أن تصبح كل ميزة مغامرة.
