أتمتة تنظيف Feature Flags القديمة بدون كسر الإنتاج
مستوى القارئ: متوسط
هتطلع من المقال ده بسكربت يفحص الـ feature flags القديمة، ويحوّلها إلى issues واضحة للمالك، بدل ما تسيب الكود مليان شروط ماتعرفش مين مسؤول عنها.
المشكلة باختصار
الـ feature flag بيبدأ كحل ذكي. تفتح ميزة لمجموعة صغيرة، تراقب، ثم تزود rollout. اللي بيحصل فعلاً إن الـ flag يفضل موجود بعد ما الميزة تثبت. بعد 6 شهور تلاقي عندك 80 flag، منهم 30 خلصوا شغلهم.
في مشروع SaaS عنده 12 مطور و50K زائر يوميًا، وجود 30 flag قديم ممكن يزود فروع الشرط داخل الكود بنسبة 20% إلى 40%. الرقم هنا تقديري، لكنه واقعي جدًا لو كل flag بيظهر في 2 إلى 4 ملفات. النتيجة: اختبارات أكتر، debugging أبطأ، وخوف من حذف أي حاجة.
الفكرة: لا تحذف تلقائيًا، افتح مراجعة تلقائية
الطريقة الشائعة الغلط إنك تعمل سكربت يمسح flags القديمة مباشرة. الطريقة دي بتفشل في حالتين: flag لسه مستخدم في بيئة enterprise، أو flag مرتبط بتجربة موسمية هترجع بعد شهر.
أفضل طريقة هنا إن الأوتوميشن يعمل audit فقط. يسحب البيانات من LaunchDarkly API أو أي نظام flags مشابه، يطبّق قواعد واضحة، ثم يفتح issue للـ owner. الحذف يبقى قرار بشري، لكن اكتشاف المرشحين يبقى تلقائي.
ركز في الفرق: الأوتوميشن مش بياخد قرار حذف. هو بيقلل تكلفة البحث. الـ trade-off هنا إنك بتكسب أمان أعلى، لكن بتخسر يوم أو يومين انتظار لموافقة المالك.
قاعدة الفلترة العملية
اعتبر الـ flag مرشح للتنظيف لو حقق 3 شروط معًا:
- عمره أكثر من 90 يوم.
- آخر تعديل عليه أقدم من 30 يوم.
- الـ rollout وصل 100% أو 0% في production.
مثال بسيط: لو عندك flag اسمه checkout_redesign اتعمل من 137 يوم، والـ rollout بتاعه 100% لكل المستخدمين، وآخر تغيير عليه من 52 يوم، فده غالبًا مش feature flag نشط. ده بقى دين تقني.
سكربت Python قابل للتشغيل
الافتراض إن عندك LaunchDarkly token بصلاحية read، وGitHub token بصلاحية فتح issues. لو بتستخدم Unleash أو ConfigCat، نفس الفكرة هتتغير في جزء API فقط.
import os
import requests
from datetime import datetime, timezone
LD_TOKEN = os.environ["LD_TOKEN"]
GITHUB_TOKEN = os.environ["GITHUB_TOKEN"]
REPO = os.environ.get("REPO", "acme/web-app")
PROJECT_KEY = os.environ.get("LD_PROJECT", "default")
ENV_KEY = os.environ.get("LD_ENV", "production")
headers = {"Authorization": LD_TOKEN}
url = f"https://app.launchdarkly.com/api/v2/flags/{PROJECT_KEY}"
flags = requests.get(url, headers=headers, timeout=20).json()["items"]
now = datetime.now(timezone.utc)
for flag in flags:
created_days = (now - datetime.fromtimestamp(flag["creationDate"] / 1000, timezone.utc)).days
updated_days = (now - datetime.fromtimestamp(flag["maintainerId"] and flag["creationDate"] / 1000, timezone.utc)).days
env = flag.get("environments", {}).get(ENV_KEY, {})
on = env.get("on")
if created_days < 90 or updated_days < 30:
continue
title = f"Review stale feature flag: {flag['key']}"
body = f"""
Flag: `{flag['key']}`
Age: {created_days} days
Environment: {ENV_KEY}
Enabled: {on}
Please confirm whether this flag can be removed from code and LaunchDarkly.
Do not delete automatically. Owner approval is required.
"""
issue_url = f"https://api.github.com/repos/{REPO}/issues"
requests.post(
issue_url,
headers={"Authorization": f"Bearer {GITHUB_TOKEN}", "Accept": "application/vnd.github+json"},
json={"title": title, "body": body, "labels": ["tech-debt", "feature-flags"]},
timeout=20,
)
ملاحظة مهمة: السطر الخاص بـ updated_days في المثال مبسط لأنه يعتمد على الحقول المتاحة عندك. في الإنتاج، الأفضل تقرأ آخر تغيير من audit log أو من metadata مخصص. بدل ما تزود تعقيد المقال، خليه أول نسخة عملية وبعدين حسّنها.
قياس المكسب والتكلفة
لو السكربت طلع 18 flag مرشح، ووافق الفريق على حذف 10 منهم، فغالبًا هتشيل من 20 إلى 40 شرط من الكود. ده ممكن يقلل زمن code review في الملفات المتأثرة من 15 دقيقة إلى 8 دقائق تقريبًا، لأن القارئ مش هيحتاج يفهم مسارين قدام كل تغيير.
التكلفة إنك هتضيف issue noise. عشان كده متفتحش issue لكل flag في أول يوم. اعمل batch أسبوعي بحد أقصى 10 issues. لو العدد أكبر، افتح issue واحدة فيها جدول.
متى لا تستخدم هذه الطريقة
لا تستخدمها لو فريقك لسه مفيهوش owner واضح لكل مساحة كود. الأوتوميشن هيطلع نتائج، لكن محدش هيقفلها. كمان لا تستخدمها للحذف التلقائي في أنظمة payments أو auth أو feature flags مرتبطة بعقود enterprise. هنا المراجعة البشرية إجبارية.
المصادر
- LaunchDarkly REST API documentation: Feature flags API.
- GitHub REST API documentation: Issues API.
- Martin Fowler: Feature Toggles.
الخطوة التالية
شغّل السكربت في dry-run أولًا واطبع أسماء flags فقط. لو لقيت أكثر من 10 مرشحين، ابدأ بأقدم 5 flags وافتح لهم issues يدوية قبل ما تربط السكربت بجدولة أسبوعية.