تطبيقنا المونوليث كان وحشًا: كيف أنقذنا “نمط التين الخانق” من جحيم التحديث المستحيل؟

يا جماعة الخير، السلام عليكم ورحمة الله وبركاته. معكم أخوكم أبو عمر.

اسمحوا لي أن أرجع بالذاكرة بضع سنوات إلى الوراء. كنا في ليلة من ليالي النشر (Deployment)، والتوتر في المكتب كان ملموسًا، لدرجة أنك تستطيع أن تقطعه بسكين. كان عندنا تطبيق ضخم، “مونوليث” بكل ما تحمله الكلمة من معنى. كنا نسميه بيننا “الوحش”. هذا الوحش كان مسؤولًا عن كل شيء في شركتنا، من إدارة المستخدمين إلى الفواتير إلى التقارير… كل شيء في كود واحد متكتل ومترابط بشكل مرعب.

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

قضينا ليلتها في إعادة النظام للنسخة القديمة، وشعرت حينها بإحباط لم أشعر به من قبل. وصلنا إلى حائط مسدود. أصبح تحديث النظام شبه مستحيل، والخوف من لمسه شلّ قدرتنا على الابتكار. هنا، بدأت رحلة البحث عن حل، عن مخرج من هذا الجحيم. وبعد بحث وقراءة، تعرفت على مفهوم غريب وجميل في نفس الوقت: نمط التين الخانق (Strangler Fig Pattern). اسم مستوحى من الطبيعة، ووعدٌ بخنق الوحش ببطء وأمان. وهذه قصتنا معه.

ما هو “الوحش” المونوليثي؟ ولماذا هو مشكلة؟

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

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

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

فكرة “الانفجار العظيم” لإعادة الكتابة: الحلم الذي يتحول إلى كابوس

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

لكن من خبرتي، يا خوي، هذا النهج غالبًا ما يفشل فشلًا ذريعًا، خاصة مع الأنظمة الكبيرة. لماذا؟

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

هنا يأتي دور الحل الأكثر حكمة وعملية.

بطل القصة: نمط التين الخانق (Strangler Fig Pattern)

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

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

  1. التحويل (Transform): نحدد جزءًا من وظائف النظام القديم (مثل إدارة ملفات المستخدمين) ونبني له خدمة جديدة مستقلة (Microservice) باستخدام تقنيات حديثة.
  2. الاعتراض (Intercept): نضع “واجهة” (Façade) أمام النظام القديم، غالبًا ما تكون Reverse Proxy أو API Gateway. هذه الواجهة تعترض الطلبات الواردة. في البداية، تمرر كل الطلبات إلى النظام القديم كالمعتاد.
  3. الخنق (Strangle): نبدأ بتوجيه الطلبات المتعلقة بالوظيفة الجديدة (ملفات المستخدمين) إلى الخدمة الجديدة بدلًا من النظام القديم. ومع الوقت، ننقل المزيد والمزيد من الوظائف إلى خدمات جديدة، ونغير مسار الطلبات إليها، حتى يتم “خنق” النظام القديم بالكامل ويصبح بالإمكان إيقافه وحذفه بأمان.

مثال عملي: كيف بدأنا بخنق “الوحش”

دعونا نأخذ مثالًا عمليًا مشابهًا لما فعلناه. لنفترض أن “الوحش” المونوليثي لديه مسار للوصول لبيانات المستخدم: /api/v1/users/:id.

الخطوة 1: بناء الواجهة (The Façade)

أول شيء فعلناه هو وضع Reverse Proxy مثل Nginx أمام تطبيقنا المونوليثي. لم يلاحظ المستخدمون أي فرق. كل الطلبات كانت تمر عبر Nginx وتصل إلى الوحش.

كان إعداد Nginx بسيطًا جدًا:


# nginx.conf

server {
    listen 80;
    server_name my-awesome-app.com;

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

هنا، monolith_app:8080 هو عنوان التطبيق القديم.

الخطوة 2: بناء الخدمة المصغرة الجديدة

بعد ذلك، قام فريق صغير ببناء خدمة جديدة ومستقلة لإدارة المستخدمين. استخدمنا Node.js وExpress لأنها سريعة ومناسبة لهذا النوع من الخدمات. هذه الخدمة لديها مسار جديد: /api/v2/users/:id.

الكود كان بسيطًا، شيء من هذا القبيل:


// user-service/index.js (مثال مبسط)
const express = require('express');
const app = express();
const port = 3000;

// مسار جديد لجلب بيانات المستخدم من قاعدة بيانات جديدة أو نفس القديمة مؤقتًا
app.get('/api/v2/users/:id', (req, res) => {
    const userId = req.params.id;
    // ... منطق جلب المستخدم من قاعدة البيانات
    const user = { id: userId, name: 'أبو عمر الجديد', email: 'abu.omar.new@example.com' };
    res.json(user);
});

app.listen(port, () => {
    console.log(`User service listening on port ${port}`);
});

الخطوة 3: بدء عملية الخنق التدريجي

الآن تأتي اللحظة السحرية. عدّلنا إعدادات Nginx ليعترض الطلبات الموجهة للمستخدمين ويوجهها إلى الخدمة الجديدة، بينما بقية الطلبات تذهب كالمعتاد إلى النظام القديم.


# nginx.conf (النسخة المحدثة)

# تعريف عناوين خدماتنا
upstream monolith_server {
    server monolith_app:8080;
}

upstream user_service_server {
    server user_service_app:3000;
}

server {
    listen 80;
    server_name my-awesome-app.com;

    # هذا هو الجزء المهم
    # أي طلب يبدأ بـ /api/v1/users سيذهب للخدمة الجديدة
    location /api/v1/users {
        # يمكن إعادة كتابة المسار ليطابق مسار الخدمة الجديدة
        rewrite /api/v1/(.*) /api/v2/$1 break;
        proxy_pass http://user_service_server;
        proxy_set_header Host $host;
    }

    # بقية الطلبات تذهب إلى الوحش القديم
    location / {
        proxy_pass http://monolith_server;
        proxy_set_header Host $host;
    }
}

بهذه الطريقة، أصبح أي طلب لـ my-awesome-app.com/api/v1/users/123 يذهب الآن إلى خدمة المستخدمين الجديدة، بينما طلب مثل my-awesome-app.com/api/v1/invoices لا يزال يذهب إلى المونوليث. لم يتغير شيء بالنسبة لواجهة المستخدم أو للمستخدم النهائي، لكننا في الخلفية بدأنا باستبدال محرك السيارة قطعة قطعة وهي لا تزال تسير!

الخطوة 4: التكرار والتنظيف

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

نصائح أبو عمر الذهبية لتطبيق النمط بنجاح

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

  • ابدأ صغيرًا وبسيطًا: لا تحاول خنق جزء معقد من النظام في البداية. اختر وظيفة بسيطة ومعزولة نسبيًا (Read-only API مثلًا) لتثبت نجاح الفكرة وتكسب ثقة الفريق والإدارة.
  • المراقبة والقياس هما عيناك: استثمر بقوة في أدوات المراقبة (Monitoring) والتسجيل (Logging). يجب أن تعرف فورًا أداء الخدمة الجديدة، وهل هناك أخطاء، وكيف يقارن أداؤها بالجزء القديم.
  • استراتيجية قاعدة البيانات: هذه هي أصعب جزئية. في البداية، قد تجعل الخدمة الجديدة تقرأ وتكتب في نفس قاعدة بيانات المونوليث (لتسهيل الأمور). لكن الهدف النهائي هو أن يكون لكل خدمة قاعدة بياناتها الخاصة. هذا يتطلب استراتيجيات لمزامنة البيانات أو نقلها، وهي قصة أخرى طويلة.
  • لا تنسَ التواصل: يجب أن يكون الفريق بأكمله على دراية بالخطة. تقسيم العمل بين فريق “صيانة القديم” وفريق “بناء الجديد” يجب أن يتم بحكمة وتنسيق.
  • استمر في الخنق: بمجرد أن تبدأ، لا تتوقف. الزخم مهم جدًا. كل وظيفة جديدة يتم ترحيلها بنجاح هي انتصار صغير يغذي الحماس للمرحلة التالية.

الخلاصة يا جماعة الخير 🏁

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

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

والله ولي التوفيق.

أبو عمر

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

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

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

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

آخر المدونات

التوسع والأداء العالي والأحمال

كان فشل خدمة واحدة يُسقط نظامنا بالكامل: كيف أنقذنا نمط ‘قاطع الدائرة’ (Circuit Breaker) من جحيم الانهيارات المتتالية؟

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

16 مايو، 2026 قراءة المزيد
التكنلوجيا المالية Fintech

كانت قراراتنا الائتمانية صندوقاً أسود: كيف أنقذنا ‘الذكاء الاصطناعي القابل للتفسير’ (XAI) من جحيم التحيز والشكاوى التنظيمية؟

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

16 مايو، 2026 قراءة المزيد
البنية التحتية وإدارة السيرفرات

كانت أعطالنا تباغتنا في منتصف الليل: كيف أنقذنا Prometheus من جحيم المراقبة التفاعلية؟

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

16 مايو، 2026 قراءة المزيد
ادارة الفرق والتنمية البشرية

طلبات الدمج تموت في الانتظار: كيف أنقذ “ميثاق مراجعة الكود” فريقنا من جحيم التأخير والجدل؟

أتذكر ذلك اليوم جيداً، طلب دمج (Pull Request) عالق لأسبوع، ونقاش حاد بين اثنين من أفضل المبرمجين حول تفصيل بسيط. كانت هذه هي القشة التي...

16 مايو، 2026 قراءة المزيد
اختبارات الاداء والجودة

كانت تحديثاتنا تكسر التصميم: كيف أنقذنا ‘اختبار التراجع البصري’ من جحيم الأخطاء المرئية؟

أشارككم قصة حقيقية من قلب المعركة البرمجية، وكيف تحولنا من فوضى الأخطاء المرئية بعد كل تحديث إلى ثقة وهدوء بفضل اختبارات التراجع البصري (Visual Regression...

16 مايو، 2026 قراءة المزيد
أتمتة العمليات

كان مطورنا الجديد ينتظر أياماً: كيف أنقذتنا ‘أتمتة إعداد البيئة’ من جحيم الأسبوع الأول الضائع؟

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

15 مايو، 2026 قراءة المزيد
نصائح برمجية

كانت إعادة المحاولة تدمر بياناتنا: كيف أنقذتنا ‘اللامتناهية’ (Idempotency) من جحيم العمليات المكررة؟

في ليلة لم أنم فيها، كانت أنظمتنا المالية تنهار بسبب عمليات دفع متكررة. أشارككم اليوم قصة كيف أنقذنا مفهوم "اللامتناهية" (Idempotency) من كارثة محققة، وكيف...

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

كانت خدماتنا تتحدث في نفس الوقت: كيف أنقذتنا ‘المعمارية القائِمَة على الأحداث’ (EDA) من جحيم الاقتران المحكم؟

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

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