أتمتة تنظيف 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,
)