前言
我一直覺得事情可以保持簡單就好,有問題改程式碼或環境變數切換就好,何必導入更複雜的套件管理與第三方服務呢?但真實情境當問題發生時不會有空閒慢慢部署與除錯,就有必要透過更完善的 Feature Flag 規劃來降低推送功能的風險,也能減少心臟病發作的機率。
所以透過這篇文章更近一步探討如何透過 OpenFeature,一款開源的 Feature Flag 實踐標準、生態與 SDK 工具打造更穩定滑順的產品功能推送體驗。
現有問題
- 開發已經完成但商業端還沒
- 功能分佈於多個服務上
- 實驗性質功能
通常實作 Feature Flag 只需要在代碼當中埋藏的一個變數或環境變數,雖然簡單可行但很難管理與追蹤,更沒辦法靈活快速的切換應對複雜突發情況。
const isJira1234FeatureFlagOn = import.meta.env.VITE_Jira_1234if (isJira1234FeatureFlagOn) { // Flag on} else { // Flag off}什麼是 OpenFeature?
OpenFeature 是開源且支援多種供應商的開放標準,用於實踐通用的 Feature Flag:
OpenFeature 詞彙表
- Evaluation API
- 允許開發者評估 Feature Flag,並使用評估結果來影響控制流程或功能。
- Evaluation Context
- 一個容器用於存放任意上下文數據(如:
user_id,region,email),這些數據可用作動態評估的基礎。
- 一個容器用於存放任意上下文數據(如:
- Providers
- 負責將提供給 Evaluation API 的參數對應到其在關聯標誌管理系統中的等效表示形式。提供者可以封裝供應商 SDK、呼叫自訂的標誌評估 REST API,甚至解析本機儲存的檔案來解析標誌值。
- Hooks
- 用於在生命週期的各個階段添加任意行為。
- Events
- 當系統觸發某些特定事件(如:Provider 已就緒、旗標設定變更、Provider 出現故障)時觸發。這讓應用程式能對環境變化做出即時反應。
如何使用 OpenFeature?
使用 OpenFeature 的過程就像是「配置插座」與「接入插頭」的過程,主要分為三個步驟:
一、配置 Provider(設定插座)
import { OpenFeature, InMemoryProvider } from '@openfeature/web-sdk';
const provider = new InMemoryProvider(flagConfig);await OpenFeature.setContext({ user_id: '123' });await OpenFeature.setProviderAndWait(provider);const flagConfig = { 'show-welcome-banner': { disabled: false, variants: { on: true, off: false }, defaultVariant: 'on', // 可選: contextEvaluator 決定 flag 結果基於 context // InMemoryProvider 特有的功能,其他 Provider 通常在服務端處理 contextEvaluator: (ctx: any) => { if (ctx.user_id === '123') return 'on'; return 'off'; } }, 'button-color': { disabled: false, variants: { green: 'green', red: 'red', gray: 'gray' }, defaultVariant: 'green', contextEvaluator: (ctx: any) => 'green' }, 'retry-count': { disabled: false, variants: { five: 5, ten: 10 }, defaultVariant: 'five' }, 'config-object': { disabled: false, variants: { production: { env: 'production', apis: ['v1', 'v2'], }, dev: { env: 'development', apis: ['v1'], } }, defaultVariant: 'production' },};二、獲取 Client(取得存取介面)
Client 是與 Feature Flag 互動的入口,可以針對不同的業務邏輯(如:user-service 或 billing)獲取不同的 Client。
const client = OpenFeature.getClient('my-app-name');三、執行評估(獲取結果)
使用 Client 提供的 API 來獲取旗標值。你需要提供旗標名稱、預設值以及選填的評估上下文。
// 動態提供上下文(如用戶 ID)const context = { targetingKey: 'user-123', region: 'asia' };
// 評估布林類型的旗標const isNewFeatureEnabled = await client.getBooleanValue( 'new-cool-feature', false, context);
if (isNewFeatureEnabled) { // 執行新功能代碼}挑選合適的方案
前面使用預設官方實踐的 InMemoryProvider web/sdk 於前端用於熟悉 OpenFeature 的大致運作邏輯,具體實踐中可以找許多現成的 Provider
也可以在先前基礎上接上後端的 flagConfig 用於動態改變 Feature Flag:
// Node.js/Expressapp.get('/api/feature-flags', authenticate, (req, res) => { const userId = req.user.id; const userRegion = req.user.region;
const flags = { 'show-welcome-banner': { disabled: false, variants: { on: true, off: false }, defaultVariant: userId === '123' ? 'on' : 'off' } };
res.json(flags);});const response = await fetch('/api/feature-flags', { headers: { 'Authorization': `Bearer ${token}` }});const flagConfig = await response.json();
const provider = new InMemoryProvider(flagConfig);await OpenFeature.setProviderAndWait(provider);雖然以上的做法可行,但通常 InMemoryProvider 主要用於測試或靜態配置,最好採用其他 Feature Flag 設施,例如 flagd與管理介面如 unleash。
// 2. 前端使用 flagd providerimport { FlagdWebProvider } from '@openfeature/flagd-web-provider';
const provider = new FlagdWebProvider({ host: 'https://your-flagd-server.com', port: 8013});
await OpenFeature.setProviderAndWait(provider);延伸閱讀
- OpenFeature Will CHANGE How You Deploy Code - Better Stack
- An Introduction to Feature Flagging & OpenFeature - CNCF [Cloud Native Computing Foundation]
- Move from Environment Variables to Feature Flags - OpenFeature