أعلام الميزات في TypeScript: كيفية تفعيل الميزات دون نشر

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

عندما يكون النشر مؤلماً: لماذا يعشق المطورون أعلام الميزات

\n\n

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

\n\n

والآن تخيل سيناريو آخر: تقوم بتفعيل الميزة الجديدة فقط لـ 5% من المستخدمين. تراقب المقاييس. ترى أن كل شيء على ما يرام. تزيد النسبة تدريجياً إلى 100%. لا توتر، ولا تراجعات، فقط ثقة هادئة.

\n\n

هذا هو سحر أعلام الميزات (وتسمى أيضاً مفاتيح الميزات، أعلام الميزات، أعلام الوظائف). في مشاريع TypeScript، تكون هذه التقنية ممتازة بشكل خاص بفضل الكتابة الثابتة والتحكم الصارم في الأنواع. اليوم سنتعرف على كيفية دمج أعلام الميزات في مشروعك، لتنام بهدوء، ويتم تفعيل الميزات دون ألم.

\n\n

ما هي أعلام الميزات بعبارات بسيطة

\n\n

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

\n\n
if (featureFlags.isEnabled('new-checkout')) {\n  // كود جديد\n} else {\n  // كود قديم\n}\n
\n\n

كل شيء عبقري بسيط، أليس كذلك؟ لكن وراء هذه البساطة تكمن أداة قوية تستخدمها Google وNetflix وFacebook وغيرها من الشركات العملاقة. إنهم لا ينشرون كل تفصيلة صغيرة - بل يديرون سلوك التطبيق من خلال لوحة تحكم مركزية.

\n\n

متى تكون أعلام الميزات ضرورية حقاً

\n\n
    \n
  • الإصدار التجريبي (Canary release) - طرح ميزة جديدة لجزء صغير من المستخدمين
  • \n
  • الإطلاق المخفي (Dark launch) - اختبار مخفي حيث توجد الميزة لكن المستخدمين لا يرونها
  • \n
  • اختبار A/B - مقارنة نسختين من الوظيفة
  • \n
  • مفتاح الإيقاف (Kill switch) - تعطيل طارئ لكود إشكالي دون نشر
  • \n
  • اختبار الإذن (Permit testing) - اختبار على المستخدمين الداخليين أو حسب الأدوار
  • \n
\n\n

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

\n\n

هندسة نظام أعلام الميزات

\n\n

قبل كتابة الكود، دعنا نفهم مما يتكون النظام الجيد لإدارة الوظائف.

\n\n

المكونات الأساسية

\n\n
    \n
  1. مخزن الأعلام - حيث يتم تخزين القيم (JSON، قاعدة بيانات، خدمة خارجية)
  2. \n
  3. الموفر (Provider) - وحدة تقوم بتحميل وتخزين الأعلام مؤقتاً
  4. \n
  5. الجزء العميل - الكود الذي يتحقق من حالة الأعلام
  6. \n
  7. لوحة التحكم - واجهة لتغيير الأعلام في الوقت الفعلي
  8. \n
\n\n

أبسط تطبيق في TypeScript

\n\n

لنبدأ بأبسط مثال. سننشئ نوعاً للأعلام وخدمة بسيطة:

\n\n
// types/feature-flags.ts\nexport type FeatureFlag = \n  | 'new-checkout'\n  | 'dark-mode'\n  | 'recommendations-v2'\n  | 'beta-chat';\n\nexport interface FeatureFlagConfig {\n  flag: FeatureFlag;\n  enabled: boolean;\n  rolloutPercentage?: number;\n  userIds?: string[];\n}\n
\n\n

الآن سنكتب الخدمة نفسها:

\n\n
// services/feature-flag.service.ts\nexport class FeatureFlagService {\n  private flags: Map<FeatureFlag, FeatureFlagConfig> = new Map();\n\n  constructor(initialFlags: FeatureFlagConfig[]) {\n    initialFlags.forEach(flag => {\n      this.flags.set(flag.flag, flag);\n    });\n  }\n\n  isEnabled(flag: FeatureFlag, userId?: string): boolean {\n    const config = this.flags.get(flag);\n    if (!config) return false;\n\n    if (!config.enabled) return false;\n\n    // التحقق من مستخدمين محددين\n    if (config.userIds && userId) {\n      return config.userIds.includes(userId);\n    }\n\n    // التوزيع حسب النسبة المئوية                

المدونات

توصيات الكتب