تطبيقي المتجانس كان وحشاً لا يمكن ترويضه: كيف أنقذني ‘نمط الخانق’ (Strangler Fig Pattern) من جحيم إعادة الكتابة الكبرى؟

أذكر ذلك اليوم جيداً، كان يوم خميس، ونهاية أسبوع عمل مرهق. كنتُ أجلس في مكتبي، أحدّق في شاشة تعرض آلاف الأسطر من الشيفرة البرمجية لتطبيقنا الرئيسي، “الرزق”. كان هذا التطبيق هو قلب شركتنا النابض، لكنه مع مرور السنين تحول من مصدر فخر إلى مصدر قلق دائم. كان تطبيقاً متجانساً (Monolith) ضخماً، وكل جزء فيه يعتمد على الآخر بشكل مرعب.

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

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

ما هو التطبيق المتجانس (Monolith)؟ ولماذا يصبح وحشاً؟

قبل أن نغوص في الحل، دعونا نفهم المشكلة. التطبيق المتجانس، أو “المونوليث”، هو تطبيق برمجي مبني كوحدة واحدة متكاملة. كل شيء – واجهة المستخدم، منطق العمل (Business Logic)، الوصول للبيانات – موجود في قاعدة كود واحدة ويتم نشره ككتلة واحدة.

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

  • الاقتران المحكم (Tight Coupling): يصبح كل جزء معتمداً على الآخر. تعديل صغير في مكان قد يسبب سلسلة من الأخطاء غير المتوقعة في أماكن أخرى (كما حدث معنا بالضبط).
  • بطء دورة التطوير: أي تغيير، مهما كان صغيراً، يتطلب اختبار وإعادة نشر التطبيق بأكمله. هذا يجعل عملية النشر بطيئة ومحفوفة بالمخاطر.
  • صعوبة التوسع (Scalability): إذا كان جزء واحد فقط من التطبيق (مثل خدمة معالجة الصور) يحتاج إلى موارد أكبر، فأنت مضطر لعمل نسخة كاملة من التطبيق وتوسيع كل شيء معه، وهو هدر كبير للموارد.
  • الحاجز التكنولوجي: أنت عالق مع التقنيات التي بدأت بها المشروع. تحديث إطار العمل (Framework) أو حتى لغة البرمجة يصبح مهمة شبه مستحيلة ومكلفة جداً.

فكرة “إعادة الكتابة الكبرى”: حلم وردي أم كابوس محقق؟

عندما تصل الأمور إلى هذا الحد من السوء، يظهر اقتراح “إعادة الكتابة الكبرى” (The Big Bang Rewrite) كحل سحري. “لنهدم البيت القديم ونبني قصراً جديداً مكانه!”. الفكرة مغرية جداً، لكنها من أخطر الفخاخ في عالم هندسة البرمجيات.

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

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

الحل السحري: نمط شجرة التين الخانقة (Strangler Fig Pattern)

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

في عالم البرمجيات، الفكرة مماثلة تماماً:

  1. نبني نظاماً جديداً (شجرة التين) حول النظام القديم (الشجرة المضيفة).
  2. نوجه الطلبات (الضوء والموارد) تدريجياً من النظام القديم إلى الجديد، وظيفة تلو الأخرى.
  3. مع مرور الوقت، “تخنق” الوظائف الجديدة نظيراتها القديمة، حتى يصبح النظام القديم مجرد هيكل فارغ يمكن إزالته بأمان.

هذا النهج يسمح لنا بتحديث نظامنا بشكل تدريجي، آمن، ودون إيقاف عجلة العمل.

كيف طبقنا ‘نمط الخانق’ خطوة بخطوة؟

هذه هي الخطة العملية التي اتبعناها لترويض وحشنا “الرزق”:

الخطوة الأولى: بناء الواجهة الخانقة (The Strangler Façade)

أول وأهم خطوة هي إنشاء واجهة أمامية (Façade) تكون هي نقطة الدخول الوحيدة لكل الطلبات الموجهة لنظامنا. هذه الواجهة هي التي ستقرر ما إذا كان الطلب سيذهب إلى التطبيق المتجانس القديم أم إلى الخدمة المصغرة الجديدة. في حالتنا، استخدمنا بروكسي عكسي (Reverse Proxy) مثل Nginx.

في البداية، كل ما يفعله هذا البروكسي هو تمرير كل الطلبات كما هي إلى التطبيق القديم.


# nginx.conf (المرحلة الأولى)

server {
    listen 80;
    server_name alrizq.com;

    # في البداية، كل الطلبات تذهب إلى المونوليث القديم
    location / {
        proxy_pass http://legacy_monolith_app:8080;
        proxy_set_header Host $host;
    }
}

الخطوة الثانية: تحديد أول ضحية (أول خدمة للانتقال)

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

نصيحة من أبو عمر: اختر فوزاً سهلاً وسريعاً في البداية (a quick win). هذا يرفع من معنويات الفريق، ويثبت للإدارة أن الخطة ناجحة، ويعطيك فرصة لتعلم وتصحيح الأخطاء في جزء قليل المخاطر من النظام.

الخطوة الثالثة: تطوير ونشر الخدمة الجديدة

قمنا ببناء خدمة مصغرة جديدة لإدارة ملفات المستخدمين. استخدمنا فيها تقنيات أحدث (Python مع FastAPI) بدلاً من الـ Java القديمة في المونوليث. هذه إحدى أروع ميزات هذا النمط: حرية اختيار التكنولوجيا الأنسب لكل خدمة. قمنا بنشر هذه الخدمة بشكل مستقل تماماً.

الخطوة الرابعة: التوجيه التدريجي للطلبات

الآن نعود إلى الواجهة الخانقة (Nginx) ونقوم بتعديلها. سنطلب منها أن توجه أي طلب يذهب إلى /api/users إلى الخدمة الجديدة، بينما باقي الطلبات تظل تذهب إلى النظام القديم.


# nginx.conf (المرحلة الثانية)

server {
    listen 80;
    server_name alrizq.com;

    # توجيه طلبات المستخدمين إلى الخدمة المصغرة الجديدة
    location /api/users {
        proxy_pass http://new_users_microservice:8000;
        proxy_set_header Host $host;
    }

    # باقي الطلبات لا تزال تذهب إلى المونوليث القديم
    location / {
        proxy_pass http://legacy_monolith_app:8080;
        proxy_set_header Host $host;
    }
}

بهذه البساطة، بدأنا عملية “الخنق”! المستخدمون لن يلاحظوا أي فرق، لكننا في الخلفية بدأنا باستبدال أجزاء المحرك القديم وهو لا يزال يعمل.

الخطوة الخامسة: اغسل وكرر (Rinse and Repeat)

بعد نجاح الخطوة الأولى، انتقلنا إلى الوظيفة التالية: نظام التقارير. ثم نظام الإشعارات. وفي كل مرة، كنا نكرر نفس العملية: بناء الخدمة الجديدة، نشرها، ثم تحديث الواجهة الخانقة لتوجيه الطلبات إليها. مع كل تكرار، كانت شجرة التين (النظام الجديد) تنمو، والمونوليث القديم (الشجرة المضيفة) يضمر وينكمش دوره.

الخطوة السادسة: إعلان وفاة القديم

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

تحديات ونصائح من قلب المعركة

الرحلة لم تكن سهلة بالكامل، وواجهتنا بعض التحديات التي أود مشاركتها معكم:

  • مزامنة البيانات: كيف تتعامل الخدمة الجديدة مع البيانات التي لا تزال في قاعدة بيانات المونوليث؟ في البداية، قد تضطر للسماح للخدمة الجديدة بالوصول إلى قاعدة البيانات القديمة (مع الحذر الشديد). الحل الأفضل على المدى الطويل هو استخدام أنماط مثل “مزامنة البيانات عبر الأحداث” (Event-driven data synchronization) حيث يقوم المونوليث بإصدار حدث (event) عند أي تغيير في البيانات، وتستمع له الخدمات الجديدة لتحديث قواعد بياناتها الخاصة.
  • المعاملات الموزعة (Distributed Transactions): ماذا لو كانت عملية واحدة تتطلب تحديث البيانات في خدمة جديدة والمونوليث في نفس الوقت؟ هذه مشكلة معقدة. يجب تجنبها قدر الإمكان عن طريق تقسيم الوظائف بشكل جيد. إذا كانت ضرورية، فابحث عن أنماط متقدمة مثل نمط “Saga”.
  • التوثيق: “وثّق كل خطوة. خلي خريطة الطريق واضحة للكل”. من السهل أن تضيع في التفاصيل. حافظ على وثيقة مركزية توضح الأجزاء التي تم نقلها، والأجزاء قيد النقل، وما تبقى في المونوليث.

الخلاصة: لا تهدم البيت القديم قبل بناء الجديد 🏡

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

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

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

أبو عمر

سجل دخولك لعمل نقاش تفاعلي

كافة المحادثات خاصة ولا يتم عرضها على الموقع نهائياً

آراء من النقاشات

لا توجد آراء منشورة بعد. كن أول من يشارك رأيه!

آخر المدونات

نصائح برمجية

كانت بياناتنا تتغير بغدر: كيف أنقذتنا ‘الكائنات غير القابلة للتغيير’ (Immutability) من جحيم الآثار الجانبية؟

أشارككم قصة حقيقية عن خطأ برمجي غامض كاد أن يصيب فريقنا بالجنون، وكيف كان الحل في مفهوم بسيط وقوي يسمى "اللامتغيرية" (Immutability). اكتشف كيف يمكن...

18 مايو، 2026 قراءة المزيد
​معمارية البرمجيات

كنا ضائعين بين المونوليث والخدمات المصغرة: كيف أنقذنا ‘المونوليث النمطي’ (Modulith) من جحيم التعقيد؟

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

18 مايو، 2026 قراءة المزيد
ذكاء اصطناعي

كانت إجابات نموذجنا من وحي الخيال: كيف أنقذنا البحث المعزز بالتوليد (RAG) من جحيم الهلوسة؟

أشارككم قصة حقيقية عن "هلوسة" نماذج الذكاء الاصطناعي وكيف تسببت في موقف محرج مع أحد العملاء. سنغوص في أعماق تقنية البحث المعزز بالتوليد (RAG)، ونشرحها...

18 مايو، 2026 قراءة المزيد
خوارزميات

كانت شخصياتنا في اللعبة تسير في حوائط: كيف أنقذتنا خوارزمية A* من جحيم المسارات الغبية؟

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

17 مايو، 2026 قراءة المزيد
تجربة المستخدم والابداع البصري

كانت واجهاتنا جزرًا معزولة: كيف أنقذنا ‘نظام التصميم’ من جحيم الفوضى البصرية؟

أشارككم قصة حقيقية من قلب المعركة البرمجية، كيف انتقلنا من فوضى الواجهات والتصاميم المتضاربة إلى نظام متناغم وموحّد. هذه رحلتنا في بناء "نظام تصميم" (Design...

17 مايو، 2026 قراءة المزيد
برمجة وقواعد بيانات

كانت تحديثات قاعدة البيانات كابوساً: كيف أنقذتنا أدوات الترحيل (Migrations) من جحيم التعديلات اليدوية؟

هل عانيت يوماً من تحديث مخطط قاعدة البيانات يدوياً بين فريقك؟ أبو عمر يشارككم قصة حقيقية حول كيف غيّرت أدوات الترحيل (Migrations) طريقة عمل فريقه،...

17 مايو، 2026 قراءة المزيد
البودكاست