النشر مقابل الإصدار: لماذا تفصل التوصيل التدريجي بين شيئين كنت تعتقد أنهما متطابقان
فريقك أنهى للتو تدفق شراء جديد. الكود مختبر، طلب السحب (Pull Request) مدمج، وخط أنابيب النشر (Deployment Pipeline) أخضر. تضغط على زر النشر. الإصدار الجديد يذهب إلى الإنتاج. الآن كل مستخدم يرى الزر المعاد تصميمه، وحقول النموذج المعاد ترتيبها، وشاشة التأكيد الجديدة.
لكن ماذا لو أردت رؤية أداء التدفق الجديد قبل عرضه على الجميع؟ ماذا لو أردت السماح فقط لـ 5% من المستخدمين بتجربته أولاً، ثم التوسع بناءً على بيانات حقيقية؟
في معظم الفرق، يحدث النشر والإصدار في نفس الوقت. عندما يصل كود جديد إلى الخادم، تصبح الميزات الموجودة بداخله متاحة للمستخدمين. لكن هذين الإجراءين لا يجب أن يكونا مرتبطين. فصلهما يمنحك مستوى من التحكم يغير طريقة تفكيرك في شحن البرمجيات.
النشر تقني. الإصدار تجريبي.
النشر (Deploy) هو فعل وضع الكود على الخادم. إنها عملية تقنية. تقوم ببناء قطعة أثرية (Artifact)، ونقلها إلى بيئة، وتشغيل الإصدار الجديد. الخادم الآن يشغل الكود الجديد.
الإصدار (Release) هو فعل جعل الميزة متاحة للمستخدمين. إنه قرار تجريبي. الكود يعمل بالفعل على الخادم، لكن الميزة مخفية خلف مفتاح. عندما تقلب ذلك المفتاح، يرى المستخدمون السلوك الجديد.
نفس النشر يمكن أن يحتوي على ميزات متعددة غير مُصدرة. يمكنك النشر مرة واحدة والإصدار تدريجياً. يمكنك أيضًا إصدار ميزة لمجموعة مستخدمين دون أخرى، كل ذلك من نفس الإصدار الجاري للتطبيق.
الرسم البياني أدناه يوضح الفصل:
هذا الفصل هو أساس التوصيل التدريجي (Progressive Delivery).
مثال عملي
تخيل أن فريقك بنى زر "اشتر الآن" جديد. إنه أكبر، وأكثر ألوانًا، وموضوع في مكان أكثر بروزًا. الفريق واثق من الكود، لكن غير متأكد من رد فعل المستخدمين الحاليين. تغيير مفاجئ في التخطيط قد يربك الأشخاص الذين يستخدمون الواجهة القديمة لأشهر.
مع التوصيل التدريجي، إليك كيفية التعامل معه:
- تنشر الإصدار الجديد إلى الإنتاج. كود الزر الجديد يعمل على الخادم، لكنه مخفي. يرى المستخدمون الزر القديم.
- تقوم بتكوين نظام أعلام الميزات (Feature Flag System) لإظهار الزر الجديد لـ 5% من المستخدمين. يتم اختيار هؤلاء المستخدمين عشوائيًا.
- بعد أسبوع، تتحقق من البيانات. المستخدمون الذين رأوا الزر الجديد أكملوا عمليات الشراء بمعدل أعلى. لم يتم الإبلاغ عن أي ارتباك.
- تزيد الإصدار إلى 50% من المستخدمين. يمر أسبوع آخر. البيانات لا تزال جيدة.
- تصدر إلى 100% من المستخدمين. الميزة الآن نشطة بالكامل.
لاحظ ما حدث: نشر واحد، إصدارات متعددة. الكود ذهب إلى الإنتاج مرة واحدة. أصبحت الميزة مرئية على مراحل، مدفوعة بسلوك المستخدم الحقيقي.
أعلام الميزات هي الآلية
لفصل النشر عن الإصدار، تحتاج إلى أعلام الميزات (Feature Flags). علم الميزة هو فرع شرطي في الكود الخاص بك يتحقق مما إذا كانت الميزة يجب أن تكون نشطة للمستخدم الحالي. يتم التحكم في العلم خارجيًا، عادةً من خلال خدمة تكوين أو منصة أعلام ميزات مخصصة.
علم ميزة بسيط يبدو هكذا في الكود:
if feature_flags.is_active("new_checkout_button", user_id):
render_new_button()
else:
render_old_button()
يمكن تبديل العلم دون نشر جديد. تقوم بتغيير التكوين، والطلب التالي من ذلك المستخدم يرى السلوك الجديد. لا تغيير في الكود، لا بناء، لا نشر.
أعلام الميزات تتيح أيضًا التجريب. يمكنك تشغيل اختبارات A/B عن طريق توجيه شرائح مستخدمين مختلفة إلى متغيرات ميزات مختلفة. مجموعة ترى زرًا أحمر، وأخرى ترى زرًا أزرق. البيانات تخبرك أيهما يعمل بشكل أفضل.
كيف يختلف هذا عن النشر الكناري والإصدار المتدرج
النشر الكناري (Canary Deployment) والإصدار المتدرج (Staged Rollout) يتعلقان بتوجيه المستخدمين إلى إصدارات مختلفة من التطبيق. تقوم بتشغيل إصدارين جنبًا إلى جنب، ويرسل موازن التحميل (Load Balancer) نسبة مئوية من الحركة إلى الإصدار الجديد. إذا حدث خطأ ما، تعيد توجيه الحركة إلى الإصدار القديم.
التوصيل التدريجي يعمل بشكل مختلف. تقوم بتشغيل إصدار واحد من التطبيق. جميع المستخدمين يصلون إلى نفس الخوادم. لكن داخل ذلك الإصدار، يرى المستخدمون المختلفون ميزات مختلفة. الفصل يحدث على مستوى الميزة، وليس على مستوى التطبيق.
هذا التمييز مهم عندما تريد إصدار ميزة بشكل مستقل عن التغييرات الأخرى في نفس النشر. مع النشر الكناري، لا يمكنك إصدار الزر الجديد لـ 5% من المستخدمين مع إبقاء بقية الإصدار الجديد مخفيًا. إما أن ترسل المستخدمين إلى الإصدار الجديد أو الإصدار القديم. مع التوصيل التدريجي، تتحكم في كل ميزة على حدة.
تكلفة أعلام الميزات
أعلام الميزات ليست مجانية. كل علم يضيف فرعًا شرطيًا إلى الكود الخاص بك. بمرور الوقت، تتراكم الأعلام. إذا لم تقم بتنظيفها، فستمتلئ قاعدة الكود الخاصة بك بشروط ميتة تجعل المنطق أصعب في القراءة والاختبار.
النمط الشائع هو استخدام علم لميزة، والتحقق من صحتها، وإصدارها بالكامل، ثم إزالة العلم في السباق التالي. لكن الفرق غالبًا ما تنسى هذه الخطوة. يظل العلم في الكود، ولا أحد يتذكر ما يتحكم فيه أو ما إذا كان لا يزال نشطًا.
الانضباط مهم هنا. تعامل مع أعلام الميزات على أنها مؤقتة بشكل افتراضي. عندما يتم إصدار ميزة بالكامل لجميع المستخدمين، قم بجدولة عمل التنظيف. إذا كنت تستخدم منصة أعلام ميزات، فإن معظم الأدوات توفر لوحات تحكم تظهر الأعلام التي تم إصدارها بالكامل وجاهزة للإزالة.
متى يكون التوصيل التدريجي منطقيًا
ليس كل فريق يحتاج إلى التوصيل التدريجي. إذا كان تطبيقك صغيرًا، وقاعدة مستخدميك متجانسة، وميزاتك بسيطة، فقد لا تستحق النفقات العامة لأعلام الميزات.
لكن التوصيل التدريجي يصبح قيمًا عندما:
- تقوم بشحن ميزات تغير سلوك المستخدم بشكل كبير.
- لديك قاعدة مستخدمين كبيرة أو متنوعة حيث تختلف ردود الفعل.
- تريد التحقق من صحة الميزات ببيانات حقيقية قبل الإصدار الكامل.
- فريقك يصدر بشكل متكرر ويريد فصل إيقاع النشر عن توقيت الإصدار.
الرؤية الرئيسية هي أن التوصيل التدريجي يمنحك أرضية وسطى بين "الشحن للجميع" و"عدم الشحن على الإطلاق". يمكنك شحن الكود، ومراقبة التأثير، وتوسيع الإصدار بناءً على الأدلة.
قائمة تحقق عملية لاعتماد التوصيل التدريجي
إذا قررت فصل النشر عن الإصدار، إليك الخطوات للبدء:
- اختر ميزة واحدة ستستفيد من التعرض التدريجي. لا تبدأ بكل ميزة.
- أضف علم ميزة لتلك الميزة. استخدم ملف تكوين بسيط أو خدمة مخصصة.
- انشر الكود مع إيقاف تشغيل العلم. تحقق من أن الميزة مخفية.
- شغل العلم لنسبة صغيرة من المستخدمين. راقب المقاييس والسجلات.
- وسع النسبة بناءً على البيانات. إذا حدث خطأ ما، أوقف تشغيل العلم فورًا.
- عندما يتم إصدار الميزة بالكامل، أزل العلم من الكود.
الخلاصة
النشر والإصدار ليسا نفس الشيء. النشر هو وضع الكود على الخوادم. الإصدار هو جعل الميزات مرئية للمستخدمين. التوصيل التدريجي يفصل بين هذين الإجراءين حتى تتمكن من شحن الكود وفقًا لجدولك الزمني وإصدار الميزات وفقًا لجدول البيانات.
في المرة القادمة التي ينهي فيها فريقك ميزة، اسأل: هل نحتاج إلى إصدار هذا للجميع الآن، أم يمكننا ترك البيانات تقرر؟