رفتن به محتوای اصلی

Feature Flags

ساخت، هدف‌گیری و ارزیابی Feature Flagهای آلفانا از طریق داشبورد و SDK.

Feature Flag به شما اجازه می‌دهد یک قابلیت را بدون deploy تازه فعال یا غیرفعال کنید و دسترسی به آن را بر اساس ویژگی‌های کاربران کنترل کنید.

مدیریت Flagها از مسیر زیر در داشبورد آلفانا انجام می‌شود:

text
/dashboard/feature-flags

این بخش با JWT محافظت می‌شود و برای ساخت، ویرایش یا تغییر وضعیت Flagها استفاده می‌شود.

SDK برای ارزیابی Flagهای همان App از endpoint زیر استفاده می‌کند:

text
POST /api/feature-flags/evaluate

این درخواست با credential مربوط به App انجام می‌شود و نتیجه ارزیابی Feature Flagهای قابل استفاده برای Visitor فعلی را در اختیار Tracker قرار می‌دهد.

ساختار Feature Flag#

هر Feature Flag یک key یکتا دارد که SDK و کد محصول از طریق آن Flag را می‌شناسند.

Key می‌تواند شامل موارد زیر باشد:

  • حروف کوچک انگلیسی
  • اعداد
  • -
  • _

برای مثال:

text
new_checkout
pricing-v2
beta_dashboard

در کنار key، هر Flag می‌تواند شامل توضیح اختیاری، وضعیت enabled و صفر یا چند Condition برای Targeting باشد.

بهتر است Key را بعد از استفاده در Production بدون دلیل جدی تغییر ندهید؛ چون همان مقدار معمولاً در کد Client و منطق محصول استفاده می‌شود.

رفتار پایه Flag#

منطق پایه Feature Flag ساده است:

  • اگر Flag خاموش باشد، نتیجه ارزیابی همیشه false است.
  • اگر Flag روشن باشد و هیچ Condition نداشته باشد، نتیجه برای همه کاربران true خواهد بود.
  • اگر Flag روشن باشد و Condition داشته باشد، نتیجه بر اساس ویژگی‌های Visitor و منطق Targeting همان Flag تعیین می‌شود.

بنابراین فعال بودن Flag لزوماً به این معنا نیست که همه کاربران آن را دریافت می‌کنند. وقتی Condition تعریف شده باشد، فقط Visitorهایی که شرایط Targeting را داشته باشند می‌توانند نتیجه فعال دریافت کنند.

Targeting#

Conditionها برای محدود کردن Feature Flag به گروه مشخصی از کاربران استفاده می‌شوند.

ویژگی‌هایی که با identify() برای Visitor ثبت می‌کنید می‌توانند در ارزیابی Flag مورد استفاده قرار بگیرند.

برای مثال:

typescript
tracker.identify({
  plan: "pro",
  role: "admin",
  country: "IR",
});

اگر Feature Flag بر اساس یکی از این ویژگی‌ها Target شده باشد، اجرای identify() باعث می‌شود SDK اطلاعات جدید کاربر را ثبت کند و ارزیابی Flagها دوباره انجام شود.

به همین دلیل، Naming ویژگی‌هایی مثل plan، role یا country را در کل محصول ثابت نگه دارید.

برای مثال، اگر Plan در بخشی از محصول با مقدار:

text
pro

و در بخش دیگری با مقدار:

text
Pro Plan

ارسال شود، این دو مقدار می‌توانند در Targeting به‌عنوان دو مقدار متفاوت در نظر گرفته شوند.

جریان SDK#

چرخه Feature Flag در SDK به این شکل انجام می‌شود:

  1. هنگام اجرای init()، Tracker ارزیابی اولیه Feature Flagها را انجام می‌دهد.

  2. نتیجه ارزیابی در snapshot فعلی Flagها نگهداری می‌شود تا کد محصول بتواند بدون درخواست جداگانه در هر بار Render به آن دسترسی داشته باشد.

  3. اگر بعداً identify() اجرا شود، ویژگی‌های جدید Visitor ثبت می‌شوند و SDK Flagها را دوباره fetch می‌کند.

  4. getFlags()، isFeatureEnabled(key) و Hookهای React از snapshot فعلی نتیجه استفاده می‌کنند.

  5. در صورت refresh شدن Flagها، subscriptionها و Hookهای مرتبط می‌توانند مقدار جدید را دریافت کنند.

این ساختار کمک می‌کند کد محصول برای هر بار بررسی یک Feature Flag مستقیماً endpoint ارزیابی را فراخوانی نکند.

خواندن Flag از Tracker#

برای دریافت snapshot همه Flagهای فعلی می‌توانید از getFlags() استفاده کنید:

typescript
const flags = tracker.getFlags();

اگر فقط وضعیت یک Flag مشخص مهم است، از isFeatureEnabled(key) استفاده کنید:

typescript
const enabled = tracker.isFeatureEnabled("new_checkout");

خروجی این متد وضعیت فعلی همان Flag را بر اساس آخرین ارزیابی موجود در Tracker نشان می‌دهد.

Refresh کردن Flagها#

اگر لازم است ارزیابی Flagها خارج از چرخه عادی SDK دوباره انجام شود، fetchFlags() در دسترس است.

typescript
await tracker.fetchFlags();

در حالت معمول، init() ارزیابی اولیه را انجام می‌دهد و identify() نیز بعد از تغییر ویژگی‌های Visitor باعث refresh شدن Flagها می‌شود.

بنابراین لازم نیست fetchFlags() را قبل از هر بار خواندن Flag اجرا کنید.

دنبال کردن تغییر Flagها#

اگر بخشی از برنامه باید هنگام تغییر snapshot Flagها واکنش نشان دهد، می‌توانید از onFlagsChange() استفاده کنید.

این subscription زمانی کاربرد دارد که نتیجه Feature Flag بعد از initialization یا شناسایی کاربر تغییر کند و بخواهید رفتار UI یا منطق Client با مقدار تازه هماهنگ شود.

در React معمولاً استفاده از Hookهای آماده انتخاب ساده‌تری است، چون rerender مرتبط با تغییر Flag را مدیریت می‌کنند.

استفاده در React#

برای دریافت وضعیت یک Feature Flag در React می‌توانید از useFeatureFlag() استفاده کنید:

tsx
import { useFeatureFlag } from "alphana-sdk/react";

export function Checkout() {
  const newCheckoutEnabled = useFeatureFlag("new_checkout");

  if (newCheckoutEnabled) {
    return <NewCheckout />;
  }

  return <CurrentCheckout />;
}

اگر به همه Flagهای فعلی نیاز دارید، useFeatureFlags() map کامل آن‌ها را در اختیار Component قرار می‌دهد.

tsx
import { useFeatureFlags } from "alphana-sdk/react";

const flags = useFeatureFlags();

Hookهای React هنگام refresh شدن Flagها می‌توانند Component را با snapshot جدید rerender کنند.

Feature Flag به‌عنوان Kill Switch#

یکی از کاربردهای Feature Flag این است که بتوانید یک قابلیت را بدون deploy تازه سریعاً غیرفعال کنید.

برای مثال، اگر یک قابلیت جدید در Production رفتار غیرمنتظره‌ای داشته باشد، می‌توانید Flag مربوط به آن را از داشبورد خاموش کنید.

وقتی Flag غیرفعال باشد، نتیجه ارزیابی آن برای همه کاربران false خواهد بود؛ حتی اگر برای آن Condition تعریف شده باشد.

این الگو می‌تواند برای قابلیت‌هایی مناسب باشد که لازم است امکان کنترل سریع وضعیت آن‌ها خارج از چرخه deploy وجود داشته باشد.

Flag بدون Condition#

اگر Feature Flag روشن باشد اما هیچ Condition برای آن تعریف نشده باشد، Flag برای همه کاربران فعال در نظر گرفته می‌شود.

برای مثال:

text
enabled: true
conditions: []

در این وضعیت، همه Visitorهایی که Flag را ارزیابی می‌کنند نتیجه true دریافت خواهند کرد.

اگر هدف شما rollout محدود است، قبل از فعال کردن Flag مطمئن شوید Conditionهای موردنیاز تعریف شده‌اند.

Flag با Condition#

وقتی یک یا چند Condition وجود داشته باشد، ارزیابی بر اساس context Visitor انجام می‌شود.

ویژگی‌های ارسال‌شده با identify() بخشی از این context هستند.

برای مثال می‌توانید یک قابلیت را فقط برای گروهی مشخص از کاربران هدف‌گیری کنید و بعد از Login یا مشخص شدن اطلاعات حساب، identify() را اجرا کنید تا ارزیابی با داده کامل‌تری انجام شود.

typescript
tracker.identify({
  plan: "enterprise",
  role: "admin",
});

بعد از این فراخوانی، SDK Flagها را دوباره fetch می‌کند تا نتیجه با ویژگی‌های جدید کاربر هماهنگ شود.

تفاوت Dashboard و Evaluation API#

مسیر مدیریت Feature Flagها و مسیر ارزیابی SDK دو نقش متفاوت دارند.

مدیریت از طریق داشبورد انجام می‌شود:

text
/dashboard/feature-flags

و به احراز هویت حساب با JWT نیاز دارد.

در مقابل، SDK برای ارزیابی Flagهای App از endpoint زیر استفاده می‌کند:

text
POST /api/feature-flags/evaluate

این endpoint برای جریان runtime SDK طراحی شده است و با App Secret همان App کار می‌کند.

بنابراین endpoint مدیریت Dashboard را نباید به‌عنوان API عمومی Feature Flag در Client استفاده کنید.

Feature Flag و Public API#

ارزیابی runtime Feature Flag از مسیر Account Public API انجام نمی‌شود.

Account API Key با پیشوند:

text
alphana_api_

برای Public API سطح حساب طراحی شده است و نباید برای ارزیابی Feature Flag داخل مرورگر استفاده شود.

SDK از credential مربوط به App و endpoint اختصاصی Feature Flag استفاده می‌کند.

توصیه‌های اجرایی#

  • برای هر Flag یک Key کوتاه، ثابت و قابل‌فهم انتخاب کنید.
  • از تغییر Key بعد از استفاده در Production خودداری کنید.
  • اگر Flag قرار است فقط برای گروه محدودی فعال شود، قبل از روشن کردن آن Conditionها را بررسی کنید.
  • propertyهای مورد استفاده در Targeting را با identify() و Naming ثابت ارسال کنید.
  • از Feature Flag برای مخفی کردن داده حساس یا اعمال Authorization استفاده نکنید؛ Flag کنترل تجربه محصول است، نه مکانیزم امنیتی.
  • برای قابلیت‌های حساس، دسترسی واقعی را همچنان در Backend اعتبارسنجی کنید.
  • Flagهای قدیمی که دیگر استفاده نمی‌شوند را از کد و Dashboard پاک‌سازی کنید تا منطق محصول در طول زمان پیچیده نشود.