يا جماعة الخير، السلام عليكم.
بتذكر قبل كم سنة، كنا في اجتماع من الاجتماعات اللي بتجيب الضغط. كل الفريق موجود، من أصغر مبرمج لأكبر مدير مشروع، والوجوه عليها ألف سؤال وسؤال. القصة كلها بتدور حول نظامنا الأساسي، “الختيار” زي ما كنا نسميه بين بعض. نظام ضخم، قديم، ومترابط بشكل بخوّف، مكتوب بتقنيات صارت من العصر الحجري البرمجي. كل ما نحاول نضيف ميزة جديدة، بنوقع في حقل ألغام. نصلّح “بَغ” (Bug) في مكان، بتطلع عشرة في مكان ثاني. عملية النشر (Deployment) كانت تحتاج ليوم كامل من التحضير والدعاء، وبعدها بنقضي يومين نصلّح المشاكل اللي طلعت بسببها.
في هذاك الاجتماع، كان المدير التقني بحكي بنبرة يائسة: “يا جماعة، لازم نلاقي حل. “الختيار” قاعد بيبتلعنا أحياء!”. كان الخيار المطروح على الطاولة هو الكابوس الأكبر لكل فريق برمجي: إعادة كتابة النظام بالكامل من الصفر. مجرد التفكير في الموضوع كان يجيب الكوابيس: سنتين أو ثلاثة من العمل بدون أي ميزات جديدة للعملاء، ومخاطرة هائلة بفشل المشروع كله. حسينا حالنا محبوسين في زنزانة الكود القديم، والمفتاح ضايع.
وقتها، وأنا قاعد بسمع النقاش المحتدم، خطرت ببالي فكرة كنت قرأت عنها في مقال لمارتن فاولر. فكرة مستوحاة من الطبيعة، من شجرة غريبة اسمها “التين الخانق”. قاطعت الاجتماع وطلبت أحكي… ومن يومها، تغير كل شيء.
ما هو المونوليث (Monolith) ولماذا كان كابوسًا؟
قبل ما نكمل القصة، خلونا نوضح شو يعني “مونوليث” للي مش ملم بالمصطلح. تخيل إنك بتبني عمارة ضخمة، لكن بدل ما تقسمها لشقق ومكاتب منفصلة، بنيتها كلها كقاعة واحدة مفتوحة. المطبخ جنب غرف النوم، والحمامات جنب مكاتب المحاسبة. كل شيء متصل بكل شيء.
هذا هو المونوليث باختصار: تطبيق واحد كبير، كل أجزائه (واجهة المستخدم، منطق العمل، الوصول للبيانات) مدمجة ومترابطة في كتلة برمجية واحدة. في البداية، بكون هالأمر بسيط وسريع، لكن مع نمو التطبيق، بيبدأ الكابوس:
- الاقتران الشديد (Tight Coupling): أي تغيير صغير في جزء، ممكن يكسر عشر أجزاء ثانية ما إلها علاقة.
- صعوبة التوسع (Scalability): إذا صار عندك ضغط على جزء معين من النظام (مثلاً، صفحة المنتجات)، لازم تعمل نسخة كاملة من “العمارة” كلها عشان تتحمل الضغط، وهذا إهدار كبير للموارد.
- بطء التطوير والنشر: أي تعديل، مهما كان بسيط، بيجبرك تختبر وتعيد نشر الوحش كله. العملية بطيئة ومحفوفة بالمخاطر.
- هشاشة النظام: خطأ واحد في كود صغير ممكن يوقف النظام بالكامل.
- سجن التقنيات: أنت محبوس في التقنيات اللي بنيت فيها النظام أول مرة. تحديث إطار العمل (Framework) أو حتى لغة البرمجة بصير مهمة شبه مستحيلة.
الحل المستحيل: فخ “إعادة الكتابة الكبرى”
أول حل بيخطر بالبال لما توصل لهالمرحلة هو: “خلونا نهدم كل شيء ونبني من جديد!”. هذا ما يسمى بالـ Big Bang Rewrite. وعلى الرغم من أنه يبدو منطقياً، إلا أنه في معظم الحالات فخ قاتل.
زي اللي بهد بيته القديم عشان يبني قصر، وضل ساكن في خيمة سنين طويلة، وبالآخر اكتشف إنه ميزانية القصر ما بتكفي.
مشاريع إعادة الكتابة الكبرى غالبًا ما تفشل للأسباب التالية:
- الوقت الطويل: تستغرق سنوات، وخلال هذه الفترة، احتياجات العمل بتتغير، والمنافسين بسبقوك.
- تجميد الابتكار: لا يمكنك إضافة ميزات جديدة للنظام القديم لأن “كل الموارد موجهة للمشروع الجديد”.
- نسيان منطق العمل: الكثير من القواعد والمنطق في النظام القديم غير موثقة، وفقط موجودة في الكود. إعادة اكتشافها وهندستها من جديد عملية معقدة جدًا.
- المخاطرة العالية: بعد كل هذا التعب، قد ينتهي بك الأمر بنظام جديد يعاني من مشاكل جديدة ومختلفة.
كنا على وشك الوقوع في هذا الفخ، لولا المنقذ الذي جاء من قلب الغابات الاستوائية.
المنقذ من الطبيعة: نمط التين الخانق (Strangler Fig Pattern)
نمط التين الخانق هو استراتيجية معمارية لتحديث الأنظمة القديمة بشكل تدريجي وآمن. الاسم مستوحى من شجرة التين الخانق، التي تبدأ حياتها كبذرة صغيرة تنمو على أغصان شجرة أخرى ضخمة (الشجرة المضيفة). مع مرور الوقت، تُنزل نبتة التين جذورها نحو الأرض، وتنمو وتلتف حول الشجرة المضيفة، وتزداد سماكة وقوة.
شيئًا فشيئًا، تخنق نبتة التين الشجرة المضيفة وتحجب عنها ضوء الشمس، حتى تموت الشجرة المضيفة وتتحلل، وتبقى شجرة التين الخانق قوية وشامخة في مكانها.
في عالم البرمجيات، الفكرة هي نفسها تمامًا:
- الشجرة المضيفة: هو نظامك المونوليث القديم (“الختيار”).
- شجرة التين الخانق: هي الخدمات والتطبيقات الجديدة والحديثة التي ستبنيها.
- العملية: بدلًا من إعادة كتابة المونوليث، نقوم ببناء وظائف جديدة كنظام حديث منفصل، ثم نبدأ بتحويل “الزوار” (الطلبات) تدريجيًا من النظام القديم إلى الجديد، قطعة قطعة، حتى “يختنق” النظام القديم ويموت بشكل طبيعي.
كيف طبقنا نمط التين الخانق؟ خطوات عملية
الحكي حلو، بس كيف التطبيق على أرض الواقع؟ اسمحولي أحكيلكم بالضبط شو عملنا، خطوة بخطوة.
الخطوة الأولى: التحديد والتحليل
أول شيء عملناه هو جلسة عصف ذهني. فتحنا “الختيار” على طاولة التشريح (مجازيًا طبعًا)، وبدأنا نحلل أجزاءه. سألنا أنفسنا: وين أكثر الأماكن اللي بتوجعنا؟ أي أجزاء من النظام عليها طلب كبير للتغيير والتطوير؟
وقع اختيارنا على نظام إدارة ملفات المستخدمين (User Profiles). كان نظامًا معزولًا نسبيًا، لكنه حيوي، والتعديل عليه كان دائمًا كابوسًا.
نصيحة أبو عمر: ما تحاول تاكل الفيل لقمة واحدة، قسمه لقم صغيرة. ابدأ بجزء من النظام يكون ذا قيمة عالية ومخاطرة منخفضة نسبيًا. لا تبدأ بالقلب النابض للنظام مباشرة.
الخطوة الثانية: بناء الواجهة الخانقة (The Strangler Façade)
هذه هي أهم قطعة في اللغز كله. الواجهة الخانقة هي عبارة عن “بروكسي عكسي” (Reverse Proxy) بسيط وضعناه أمام “الختيار”. كل الطلبات من المستخدمين صارت تمر من خلال هذا البروكسي أولاً، قبل أن تصل إلى النظام القديم.
في البداية، كل ما كان يفعله هذا البروكسي هو تمرير كل الطلبات كما هي إلى المونوليث. لم يتغير شيء على المستخدم، لكننا الآن أصبحنا نتحكم في بوابة الدخول الرئيسية.
يمكن بناء هذه الواجهة باستخدام أدوات مثل Nginx, Traefik, أو حتى بكتابة تطبيق بسيط باستخدام Express.js (Node.js) أو أي إطار عمل آخر.
// مثال بسيط جدًا لواجهة خانقة باستخدام Express.js
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const app = express();
const MONOLITH_URL = "http://localhost:8080"; // عنوان النظام القديم
// في البداية، كل الطلبات تذهب إلى المونوليث
app.use('/', createProxyMiddleware({ target: MONOLITH_URL, changeOrigin: true }));
app.listen(3000, () => {
console.log('Strangler Façade is running on port 3000');
});
الخطوة الثالثة: التحويل (Transform) – بناء الخدمة الجديدة
الآن بدأ العمل الممتع. قمنا ببناء خدمة مصغرة (Microservice) جديدة لإدارة ملفات المستخدمين. استخدمنا أحدث التقنيات التي كنا نحلم بها: Node.js، قاعدة بيانات MongoDB، وبنية تحتية قائمة على الحاويات (Docker). كان الفريق متحمسًا جدًا لأنه أخيرًا يكتب كودًا نظيفًا وحديثًا.
الخطوة الرابعة: الخنق (Strangle) – إعادة توجيه المسارات
بعد أن أصبحت خدمة ملفات المستخدمين الجديدة جاهزة، حان وقت “الخنق”. عدّلنا على الواجهة الخانقة (البروكسي) لتصبح أذكى قليلاً.
الآن، عندما يأتي طلب إلى مسار معين (مثلاً /api/users أو /api/profiles)، تقوم الواجهة بتوجيهه إلى الخدمة المصغرة الجديدة، بينما كل الطلبات الأخرى تستمر في الذهاب إلى “الختيار” كالمعتاد.
// ... الكود السابق
const NEW_PROFILE_SERVICE_URL = "http://localhost:4000"; // عنوان الخدمة الجديدة
// توجيه طلبات ملفات المستخدمين للخدمة الجديدة
app.use('/api/users', createProxyMiddleware({ target: NEW_PROFILE_SERVICE_URL, changeOrigin: true }));
app.use('/api/profiles', createProxyMiddleware({ target: NEW_PROFILE_SERVICE_URL, changeOrigin: true }));
// باقي الطلبات لا تزال تذهب إلى المونوليث
app.use('/', createProxyMiddleware({ target: MONOLITH_URL, changeOrigin: true }));
// ...
وبهذه البساطة، أول جزء من النظام القديم تم “خنقه” بنجاح واستبداله ببديل حديث دون أن يشعر المستخدم بأي شيء!
الخطوة الخامسة: التكرار والمراقبة (Iterate and Monitor)
الجمال في هذه الطريقة هو أنها تكرارية. بعد نجاح خطوتنا الأولى، انتقلنا للجزء التالي: نظام الطلبات (Orders). كررنا نفس العملية: بناء خدمة جديدة، وتحديث الواجهة الخانقة لتوجيه مسارات الطلبات /api/orders للخدمة الجديدة.
مع كل تكرار، كانت شجرة التين تكبر وتلتف أكثر، والمونوليث القديم يصغر ويتقلص دوره. ومع كل خطوة، كنا نراقب الأداء، ونجمع السجلات (Logs)، ونتأكد أن كل شيء يعمل بسلاسة. المراقبة جزء لا يتجزأ من هذه العملية.
تحديات ونصائح من قلب المعركة
الطريق لم يكن مفروشًا بالورود، وواجهتنا تحديات تعلمنا منها الكثير:
- مزامنة البيانات: كيف تتعامل مع البيانات المشتركة بين النظام القديم والجديد؟ هذا أكبر تحدٍ. في بعض الحالات، كانت الخدمة الجديدة تقرأ البيانات من قاعدة بيانات المونوليث مباشرة (للقراءة فقط). وفي حالات أخرى، استخدمنا نمطًا يسمى “فرع بالاستنساخ” (Branch by Abstraction) أو أنظمة طوابير الرسائل (Message Queues) لمزامنة التغييرات بين النظامين.
- المصادقة والجلسات (Authentication & Sessions): كان علينا التأكد من أن المستخدم الذي يسجل دخوله في المونوليث يُعتبر مسجلاً دخوله في الخدمات الجديدة أيضًا. غالبًا ما يتم حل هذا على مستوى الواجهة الخانقة (البروكسي) عبر تمرير التوكنز (Tokens) أو ترويسات المصادقة (Auth Headers).
- الثقافة التنظيمية: التحول لم يكن تقنيًا فقط، بل كان ثقافيًا. تطلب الأمر من الفريق تبني ممارسات الـ DevOps، والنشر المستمر (CI/CD)، والعمل كفرق صغيرة ومستقلة.
نصيحة أبو عمر الذهبية: كن صبورًا. العملية هذه مش سباق سرعة، هذه ماراثون. المهم تمشي بخطوات ثابتة وصحيحة. احتفل بالانتصارات الصغيرة (مثل خنق أول مسار بنجاح)، فهذا يعطي الفريق دفعة معنوية هائلة للمواصلة.
الخلاصة: من الظلام إلى النور 🌅
بعد ما يقارب العامين من تطبيق نمط التين الخانق، كان “الختيار” قد تحول إلى هيكل عظمي. معظم وظائفه الحيوية كانت تعمل الآن كخدمات مصغرة حديثة، سريعة، وقابلة للتطوير. في النهاية، أطفأنا آخر جزء من المونوليث في احتفال صغير. لم تكن هناك إعادة كتابة كبرى، ولم يتوقف العمل يومًا واحدًا، ولم نخسر مستخدمًا واحدًا.
نمط التين الخانق ليس حلاً سحريًا، بل هو استراتيجية ذكية وواقعية. إنه يعترف بحقيقة أن الأنظمة القديمة جزء من واقعنا، ويقدم لنا طريقًا آمنًا وتدريجيًا للخروج من ظلامها إلى نور التقنيات الحديثة.
تذكروا يا جماعة، أفضل طريقة لتحديث نظام قديم هي ألا تحدثه كله مرة واحدة. ابدأ بخنق جزء صغير منه اليوم، وغدًا ستجد نفسك قد بنيت نظامًا جديدًا وقويًا على أنقاضه. والله ولي التوفيق.