عندما لا يعني نشر الكود إطلاق الميزات
فريقك أنهى للتو إعادة هيكلة كبيرة لخوارزمية البحث. الكود مختبر، تمت مراجعته، ونُشر في الإنتاج. لكن إليك المفاجأة: لا أحد يستخدمه بعد. الخوارزمية الجديدة موجودة على الخادم، مُجمَّعة وجاهزة، لكن كل مستخدم لا يزال يحصل على نتائج من الخوارزمية القديمة. هذا مقصود.
قد يبدو هذا السيناريو غريبًا إذا كنت معتادًا على عمليات النشر حيث كل تغيير يؤثر فورًا على المستخدمين. لكنه في الواقع نمط قوي يفصل بين شيئين تعاملهما معظم الفرق كشيء واحد: نشر الكود وإطلاق الميزات.
المشكلة التي لا يحلها النشر وحده
عمليات النشر التدريجي (Canary) والإصدارات المرحلية تحل مشكلة واحدة بشكل جيد: تتحكم في كمية الحركة التي تصل إلى إصدار جديد من تطبيقك. ترسل 5% من المستخدمين إلى الإصدار الجديد، تراقب الأخطاء، ثم ترفع النسبة إلى 100%. هذا يعمل عندما تريد طرح إصدار كامل بشكل تدريجي.
لكن ماذا لو أردت تفعيل ميزة معينة لمستخدمين محددين فقط، دون نشر إصدارات مختلفة من الكود؟ ربما تريد أن يرى المختبرون الداخليون نتائج البحث الجديدة بينما يبقى الجميع على الخوارزمية القديمة. ربما تريد تفعيل ميزة لمستخدمين في منطقة معينة أولاً. ربما تريد اختبار تدفق دفع جديد مع 10% من المستخدمين، ولكن فقط إذا كانوا على التطبيق المحمول.
النشر التدريجي والإصدارات المرحلية لا يحلان هذه المشكلة. يعملان على مستوى الإصدار، وليس على مستوى الميزة. أنت بحاجة إلى شيء يعيش داخل الكود ويتخذ قرارات لكل طلب، لكل مستخدم، لكل جلسة.
Feature Flags: طبقة تحكم داخل الكود
Feature flags هي فحوصات شرطية في الكود تحدد ما إذا كانت الميزة يجب أن تكون نشطة لمستخدم معين. الفكرة الأساسية هي أن الشرط لا يعتمد على أي إصدار من الكود قيد التشغيل. يعتمد على إعدادات يمكن تغييرها في أي وقت، دون إعادة نشر.
إليك كيف يبدو ذلك عمليًا. فريقك ينهي خوارزمية البحث الجديدة. الكود يُنشر في الإنتاج كجزء من أحدث إصدار. لكن داخل دالة البحث، يوجد فحص:
إليك مثال بلغة JavaScript لنفس النمط:
const featureFlags = require('./feature-flags');
function search(query, user) {
if (featureFlags.isEnabled('new-search', user)) {
return newSearchAlgorithm(query);
} else {
return oldSearchAlgorithm(query);
}
}
// العلم معطل افتراضيًا، لذا كل المستخدمين يمرون بالمسار القديم.
// عندما تكون جاهزًا، قم بتشغيل العلم للمختبرين عبر لوحة التحكم.
if feature_flag.is_active("new_search_algorithm", user):
return new_search_algorithm(query)
else:
return old_search_algorithm(query)
العلم معطل افتراضيًا. كل مستخدم لا يزال يحصل على الخوارزمية القديمة، حتى لو كان الكود الجديد موجودًا على نفس الخادم. عندما تكون مستعدًا، تقوم بتشغيل العلم للمختبرين الداخليين. يبدأون في رؤية نتائج جديدة. تراقب سلوكهم. إذا ظهر شيء خاطئ، تقوم بإيقاف تشغيل العلم. لا استرجاع. لا إعادة نشر. مجرد تغيير إعدادات يدخل حيز التنفيذ في ثوانٍ.
لماذا فصل النشر عن الإطلاق؟
الفائدة الأولى نفسية وعملية في نفس الوقت: النشر يتوقف عن كونه حدثًا عالي المخاطر. عندما ينشط كل نشر جميع التغييرات فورًا، تقوم بتجميع العمل، تختبر بشكل مكثف، وتحبس أنفاسك أثناء نوافذ الإصدار. عندما يكون النشر والإطلاق منفصلين، يمكنك النشر يوميًا أو حتى عدة مرات في اليوم، مع العلم أن الكود الجديد يبقى خاملاً حتى تقوم بتشغيله صراحةً.
الفائدة الثانية هي التحكم الدقيق. Feature flags ليست مجرد تشغيل أو إيقاف للجميع. يمكنك تحديد قواعد الاستهداف:
- نشطة للمستخدمين بمعرفات محددة
- نشطة للمستخدمين في مناطق معينة
- نشطة للمختبرين الداخليين أو مستخدمي النسخة التجريبية
- نشطة لنسبة مئوية من إجمالي المستخدمين
هذه القواعد يمكن أن تتراكم. قد تبدأ بـ 5% من المستخدمين، تراقب ليوم، تزيد إلى 25%، ثم إلى 50%، ثم إلى 100%. كل تغيير يحدث عبر لوحة تحكم أو استدعاء API. لا تغييرات في الكود. لا عمليات نشر.
الفائدة الثالثة هي مفتاح الإيقاف. عندما تسبب ميزة مشاكل بعد تفعيلها، يمكنك تعطيلها من مكان واحد. هذا أسرع بكثير من انتظار اكتمال خط أنابيب الاسترجاع. في ثوانٍ، الميزة الإشكالية تتوقف، ويعود المستخدمون إلى السلوك القديم. لحوادث الإنتاج، تلك الثواني مهمة.
التكلفة الخفية: ديون الأعلام (Flag Debt)
Feature flags تضيف تعقيدًا إلى قاعدة الكود. كل علم يقدم فرعًا شرطيًا يجب صيانته. إذا قام فريقك بإنشاء أعلام ولم يزلها أبدًا، فسيمتلئ الكود بشروط ميتة لا يتذكرها أحد.
سيناريو شائع: يتم إنشاء علم ميزة لتدفق دفع جديد. يتم طرح التدفق بنجاح لـ 100% من المستخدمين. كود الدفع القديم لا يزال موجودًا، محميًا بعلم معطل دائمًا. بعد أشهر، مطور جديد يرى العلم ويتساءل إذا كان لا يزال ذا صلة. لا أحد يعرف. يبقى العلم لأن إزالته تبدو محفوفة بالمخاطر.
هذا هو ديون الأعلام، وهي التكلفة التشغيلية الرئيسية لـ feature flags. الأعلام تحتاج إلى دورة حياة: إنشاء، تفعيل، مراقبة، تنظيف. عندما يتم طرح ميزة بالكامل لجميع المستخدمين، يجب إزالة مسار الكود القديم والعلم. العلم أدى غرضه. إبقاؤه يضيف فقط عبئًا معرفيًا ويزيد من مساحة الأخطاء المحتملة.
اختيار نهج إدارة الأعلام
للفرق الصغيرة أو حالات الاستخدام البسيطة، يمكن أن تبدأ feature flags كمتغيرات بيئة أو ملفات إعدادات يقرأها التطبيق عند بدء التشغيل. هذا يعمل ولكن له قيد: تغيير علم يتطلب إعادة تشغيل أو إعادة تحميل الإعدادات.
للفرق الأكبر أو قواعد الاستهداف الأكثر تعقيدًا، توفر منصات إدارة أعلام مخصصة مثل LaunchDarkly أو Split أو Flagr تقييمًا فوريًا للأعلام، واستهداف المستخدمين، وسجلات التدقيق. هذه المنصات تتيح لك تغيير الأعلام من لوحة التحكم ورؤية التأثير فورًا عبر جميع النسخ قيد التشغيل.
الاختيار الصحيح يعتمد على حجمك. ابدأ ببساطة. أضف التعقيد فقط عندما تحتاجه.
قائمة تحقق عملية لاستخدام Feature Flags
- لكل علم مالك واضح وهدف موثق في مكان مرئي
- للأعلام تاريخ إزالة مخطط أو شرط تفعيل (مثل "إزالة عند وصول الطرح إلى 100%")
- تتم إزالة مسارات الكود القديمة عندما لا يعود العلم مطلوبًا
- يتم تسجيل تغييرات الأعلام مع من غير وماذا ومتى
- للأعلام الحرجة لوحة مراقبة تظهر من يرى أي متغير
- لدى الفريق إيقاع تنظيف منتظم للأعلام القديمة
الخلاصة
Feature flags تمنحك طبقة تحكم تعمل بشكل مستقل عن خط أنابيب النشر الخاص بك. النشر التدريجي والإصدارات المرحلية تتحكم في كمية الحركة التي تصل إلى إصدار جديد. Feature flags تتحكم في أي المستخدمين يرون أي ميزات داخل نفس الإصدار. استخدمهما معًا، وستحصل على تحكم دقيق في كيفية وصول التغييرات إلى مستخدميك. فقط تذكر أن كل علم تنشئه هو قطعة من الديون الفنية التي تحتاج إلى التنظيف عندما ينتهي عملها.