يا جماعة الخير، السلام عليكم ورحمة الله.
اسمحوا لي اليوم أحكي لكم قصة صارت معي قبل كم سنة، قصة فيها عذاب وخوف، بس نهايتها كانت نصر كبير إلنا كفريق تطوير. كنا وقتها شغالين على نظام تجارة إلكترونية ضخم، نظام بنى الشركة حرفيًا وكان مصدر دخلها الأساسي. إحنا كنا نسميه بين بعض “الختيار”، لأنه كان قديم، كبير، وكله أسرار. أي حدا جديد ييجي على الفريق كان أول تحذير يسمعه: “دير بالك تقرّب من الختيار!”.
هذا “الختيار” كان عبارة عن تطبيق “مونوليث” (Monolith) مكتوب بلغة برمجة وتقنيات عفا عليها الزمن. كان ناجحًا جدًا، لكن تطوير أي ميزة جديدة فيه صار كابوس. أتذكر مرة طلب منا قسم التسويق تعديلًا بسيطًا جدًا في صفحة الدفع، مجرد إضافة حقل جديد لكود خصم. الشغلة اللي المفروض تاخذ ساعتين، أخذت معنا أسبوعين من الاختبارات والترقيع. ولما أطلقنا التحديث، فجأة وبدون سابق إنذار، توقف نظام إدارة المخزون عن العمل! نعم، تغيير في واجهة المستخدم عطّل جزءًا حساسًا في الخلفية. يومها، الموقع كله وقع في وقت الذروة، وخسرنا آلاف الدولارات. كانت ليلة ما يعلم فيها إلا ربنا.
بعد هداك اليوم، صار الكل يخاف يلمس “الختيار”. دخلنا في حالة سمّيتها “التجميد التطويري”. الإدارة خايفة من أي تغيير، والمبرمجين محبطين وعاجزين. كنا في جحيم حقيقي، إلى أن وجدنا ضوءًا في نهاية النفق، ضوءًا اسمه “نمط الخانق”.
ما هو المونوليث (Monolith)؟ ولماذا يصبح وحشاً؟
قبل ما نكمل القصة، خلونا نوضح شو يعني “مونوليث” للي مش ملمّ بالمصطلح. ببساطة، المونوليث هو تطبيق تكون كل أجزائه (واجهة المستخدم، منطق العمل، الوصول للبيانات) مرتبطة ببعضها في كتلة برمجية واحدة وقاعدة بيانات واحدة. زي ما بنحكي، “كله في طنجرة واحدة”.
في بداية أي مشروع، هذا الأسلوب بيكون ممتاز: سهل التطوير، سهل الاختبار، وسهل النشر. لكن مع مرور الوقت ونمو التطبيق، تبدأ المشاكل بالظهور، ويتحول هذا الصديق الوفي إلى وحش كاسر:
- الاقتران المحكم (Tight Coupling): كل أجزاء النظام معتمدة على بعضها. تغيير صغير في مكان قد يسبب كارثة في مكان آخر لا تتوقعه (زي ما صار معنا).
- صعوبة التوسع (Scalability Issues): إذا كان عندك ضغط على جزء معين من النظام (مثل خدمة البحث)، تضطر لعمل نسخة كاملة من التطبيق الضخم لتتحمل الضغط، وهذا هدر كبير للموارد.
- الحاجز التكنولوجي (Technology Stack Lock-in): أنت محبوس في التقنيات القديمة التي بني بها النظام. صعب جدًا أنك تجرب لغة برمجة جديدة أو قاعدة بيانات أحدث في جزء من النظام دون إعادة كتابة كل شيء.
- بطء التطوير والنشر (Slow Development & Deployment): أي تعديل يتطلب اختبار التطبيق بأكمله، وعملية النشر (Deployment) تصبح عملية خطيرة ومعقدة تستغرق ساعات أو حتى أيامًا.
هذه المشاكل هي التي أدخلتنا في “التجميد التطويري”، حيث يصبح الخوف من التغيير أكبر من الرغبة في التطوير.
معضلة إعادة الكتابة: “الانفجار العظيم” أم الخطوات المدروسة؟
عندما وصلنا لهذه المرحلة، كان أول اقتراح على الطاولة هو ما يسمى “إعادة الكتابة من الصفر” أو “Big Bang Rewrite”. الفكرة كانت مغرية: دعونا نرمي “الختيار” في سلة المهملات، ونبني نظامًا جديدًا لامعًا بأحدث التقنيات. لكن خبرتي علمتني أن هذا الطريق محفوف بالمخاطر القاتلة.
لماذا؟ لأن إعادة كتابة نظام ضخم من الصفر تستغرق شهورًا طويلة، إن لم تكن سنوات. خلال هذه الفترة، فريقك سيعمل على بناء شيء لا يدرّ أي دخل، بينما النظام القديم لا يزال بحاجة إلى صيانة. نسبة فشل هذه المشاريع عالية جدًا، فإما أن تنتهي الميزانية، أو تتغير متطلبات العمل، أو ببساطة، ينتهي بك الأمر بنسخة جديدة من نفس الوحش القديم لكن بتقنيات أحدث.
نصيحة من أبو عمر: إعادة الكتابة من الصفر هي الملاذ الأخير، لا تلجأ إليها إلا إذا استنفدت كل الحلول الأخرى. الخطر والتكلفة أعلى بكثير مما تتخيل.
كنا بحاجة إلى طريقة تمكننا من تحديث نظامنا تدريجيًا، قطعة قطعة، دون إيقاف العمل ودون مخاطرة عالية. وهنا يأتي دور المنقذ.
المنقذ: نمط الخانق (Strangler Fig Pattern)
ما هو هذا النمط؟ ومن أين جاءت التسمية؟
الاسم مستوحى من شجرة “التين الخانق” التي تنمو في الغابات الاستوائية. هذه الشجرة تبدأ حياتها كبذرة صغيرة على أغصان شجرة قديمة ضخمة. ثم تبدأ بإنزال جذورها نحو الأرض، وتنمو ملتفة حول الشجرة المضيفة. مع مرور الزمن، تزداد سماكة وقوة، وتخنق الشجرة القديمة التي بداخلها حتى تموت وتتحلل، تاركةً وراءها شجرة التين الخانق قوية وشامخة مكانها.
في عالم البرمجيات، الفكرة مماثلة تمامًا. بدلًا من إعادة كتابة المونوليث مرة واحدة، نقوم ببناء خدمات جديدة (مايكروسيرفس) حوله. ثم نضع “واجهة” أو “بروكسي” أمام النظام القديم. هذه الواجهة تعترض الطلبات القادمة من المستخدمين وتوجهها: إما إلى الخدمة الجديدة إذا كانت الوظيفة المطلوبة قد تم بناؤها، أو إلى المونوليث القديم إذا لم تكن كذلك. مع مرور الوقت، نقوم “بخنق” المزيد من الوظائف من المونوليث ونقلها إلى خدمات جديدة، حتى يصبح المونوليث فارغًا أو صغيرًا جدًا ويمكن التخلص منه بأمان.
كيف طبقنا نمط الخانق خطوة بخطوة؟ (الدليل العملي)
هذا هو الجزء الممتع. لم تكن العملية سهلة، لكنها كانت منهجية وآمنة. إليكم الخطوات التي اتبعناها:
الخطوة الأولى: تحديد الواجهة الخانقة (The Facade)
أول وأهم خطوة هي وضع طبقة “الخانق” نفسها. هذه الطبقة هي التي ستتحكم في توجيه الطلبات. يمكن أن تكون هذه الطبقة عبارة عن Reverse Proxy مثل Nginx أو HAProxy، أو API Gateway مثل Kong أو Tyk، أو حتى تطبيق بسيط كتبته بنفسك.
نحن اخترنا استخدام Nginx لبساطته وقوته. كل ما فعلناه في البداية هو تعديل إعدادات الـ DNS لتوجيه كل الترافيك إلى سيرفر Nginx الجديد، والذي بدوره كان يمرر كل الطلبات كما هي إلى المونوليث القديم.
# nginx.conf
server {
listen 80;
server_name www.our-e-commerce.com;
location / {
# في البداية، كل شيء يمر إلى النظام القديم
proxy_pass http://legacy_monolith_server:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
في هذه المرحلة، لم يتغير شيء بالنسبة للمستخدم، لكننا أصبحنا نملك نقطة تحكم مركزية.
الخطوة الثانية: اختيار القطعة الأولى (Picking the First Slice)
كانت هذه خطوة استراتيجية. نصيحتي دائمًا: ابدأ بجزء صغير، قليل المخاطرة، ويسهل فصله. الأجزاء التي تقوم بالقراءة فقط (Read-only) هي مرشح مثالي.
في حالتنا، اخترنا وحدة “المنتجات المقترحة” (Product Recommendations) على الصفحة الرئيسية. لم تكن هذه الوحدة جزءًا من عملية الشراء الأساسية، وإذا فشلت، فلن يتوقف الموقع عن العمل. كانت فرصة ممتازة لنا لنتعلم ونختبر العملية.
الخطوة الثالثة: بناء الخدمة الجديدة (Building the New Service)
هنا بدأت المتعة الحقيقية. قمنا ببناء خدمة مصغرة (microservice) جديدة باستخدام Node.js و Elasticsearch، تقنيات حديثة ومناسبة جدًا لهذه المهمة، على عكس تقنية Java القديمة في المونوليث. قمنا بوضع هذه الخدمة في Docker container ونشرها بسهولة. كان الفريق متحمسًا جدًا لاستخدام أدوات جديدة وبناء شيء نظيف من الصفر.
الخطوة الرابعة: التوجيه التدريجي (The Switchover)
بعد أن أصبحت الخدمة الجديدة جاهزة، عدنا إلى الواجهة الخانقة (Nginx) وقمنا بتعديل بسيط. أخبرنا Nginx أن أي طلب يذهب إلى مسار /api/recommendations يجب أن يتم توجيهه إلى خدمتنا الجديدة، بينما باقي الطلبات تذهب كالمعتاد إلى المونوليث.
# nginx.conf (بعد التعديل)
server {
listen 80;
server_name www.our-e-commerce.com;
# توجيه الطلبات للخدمة الجديدة
location /api/recommendations {
proxy_pass http://new_recommendation_service:3001;
# ... إعدادات البروكسي الأخرى
}
# باقي الطلبات تذهب للنظام القديم
location / {
proxy_pass http://legacy_monolith_server:8080;
# ... إعدادات البروكسي الأخرى
}
}
بمجرد تطبيق هذا الإعداد، بدأنا نخدم توصيات المنتجات من الخدمة الجديدة فائقة السرعة، بينما باقي الموقع يعمل كالمعتاد. كان انتصارًا صغيرًا لكنه أعطانا دفعة معنوية هائلة.
الخطوة الخامسة: التعامل مع البيانات (The Data Problem)
هذه هي أصعب خطوة في العادة. كيف تتعامل الخدمة الجديدة مع البيانات التي لا تزال موجودة في قاعدة بيانات المونوليث الضخمة؟ هناك عدة استراتيجيات، وما فعلناه هو مزيج منها:
- مزامنة البيانات (Data Synchronization): بالنسبة لخدمة التوصيات، بنينا عملية صغيرة (ETL script) تقوم بنسخ بيانات المنتجات بشكل دوري من قاعدة البيانات القديمة إلى قاعدة بيانات Elasticsearch الخاصة بالخدمة الجديدة. هذا سمح للخدمة الجديدة أن تكون مستقلة تمامًا في وقت التشغيل.
- واجهة برمجة تطبيقات فوق القاعدة القديمة (API over Old DB): لاحقًا، عندما بدأنا بخنق أجزاء أكثر تعقيدًا مثل “ملف المستخدم”، لم نتمكن من نسخ كل البيانات. الحل كان أن الخدمة الجديدة (خدمة المستخدمين) كانت تتواصل مع المونوليث عبر API للحصول على البيانات التي لم تهاجر بعد.
الخطوة السادسة: التكرار والخنق (Rinse and Repeat)
بعد نجاح الخطوة الأولى، كررنا العملية. القطعة التالية كانت “البحث عن المنتجات”، ثم “ملفات المستخدمين”، ثم “سلة المشتريات”. مع كل وظيفة جديدة “نخنقها” من المونوليث وننقلها إلى خدمة مصغرة، كان “الختيار” يصغر حجمًا وأهمية، بينما ينمو نظامنا الجديد الموزع ويزداد قوة ومرونة. استغرقت العملية بأكملها حوالي 18 شهرًا، لكننا كنا نطلق ميزات جديدة ونحسن الأداء طوال هذه الفترة، دون أن نشعر بالخوف أو التجمد.
نصائح من قلب المعركة (من خبرة أبو عمر)
- ابدأ صغيراً وبسيطاً: لا تحاول خنق الجزء الأكثر تعقيدًا في نظامك أولاً. اختر انتصارًا سهلاً لرفع معنويات الفريق وكسب ثقة الإدارة.
- المراقبة هي مفتاحك (Monitoring is Key): استثمر بقوة في أدوات المراقبة والتنبيه (Monitoring & Alerting). يجب أن تعرف أداء الواجهة الخانقة، والخدمات الجديدة، والمونوليث القديم في كل لحظة. أدوات مثل Prometheus و Grafana و Sentry لا تقدر بثمن.
- لا تستخف بالبيانات: مشكلة ترحيل ومزامنة البيانات هي الجزء الأصعب والأكثر استهلاكًا للوقت. ارسم خطة واضحة للبيانات قبل أن تكتب سطر كود واحد في خدمتك الجديدة.
- جهز فريقك: نمط الخانق هو تغيير ثقافي بقدر ما هو تقني. فريقك بحاجة لتعلم مفاهيم جديدة مثل المايكروسيرفس، والـ CI/CD، والحاويات (Containers). استثمر في تدريبهم.
- الشغلة بدها نفس طويل: تذكر، هذه رحلة طويلة. ستواجه تحديات ومشاكل غير متوقعة. كن صبورًا، احتفل بالانتصارات الصغيرة، ولا تفقد الهدف الأسمى من أمام عينيك.
الخلاصة: من وحش متجمد إلى نظام حيوي مرن
في النهاية، وبعد رحلة طويلة، تحول “الختيار” الذي كان مصدر رعب وإحباط إلى مجرد خدمة صغيرة واحدة ضمن عشرات الخدمات التي تشكل نظامنا الجديد. أصبحنا قادرين على تطوير ونشر ميزات جديدة في ساعات بدلاً من أسابيع، وتوسيع نطاق الخدمات التي عليها ضغط بشكل مستقل، وتجربة تقنيات جديدة دون خوف. لقد حررنا أنفسنا من “جحيم التجميد التطويري”.
نمط الخانق ليس حلاً سحريًا، بل هو استراتيجية منهجية وعملية تتطلب تخطيطًا وصبرًا. لكنه، في رأيي، أفضل وأسلم طريقة لترويض الوحوش الموروثة وإعادة الحياة إلى الأنظمة التي اعتقدنا أنها ماتت إكلينيكيًا. فلا يوجد نظام كبير لدرجة أننا لا نستطيع ترويضه خطوة بخطوة. 💪🚀
أتمنى أن تكون هذه التجربة مفيدة لكم. إذا كان لديكم أي أسئلة، فتفضلوا بطرحها. بالتوفيق في معارككم البرمجية!