Stable Product Feature Releases through OpenFeature

透過 OpenFeature 實踐穩定產品功能發布

前言

我一直覺得事情可以保持簡單就好,有問題改程式碼或環境變數切換就好,何必導入更複雜的套件管理與第三方服務呢?但真實情境當問題發生時不會有空閒慢慢部署與除錯,就有必要透過更完善的 Feature Flag 規劃來降低推送功能的風險,也能減少心臟病發作的機率。

所以透過這篇文章更近一步探討如何透過 OpenFeature🔗,一款開源的 Feature Flag 實踐標準、生態與 SDK 工具打造更穩定滑順的產品功能推送體驗。

現有問題

  • 開發已經完成但商業端還沒
  • 功能分佈於多個服務上
  • 實驗性質功能

通常實作 Feature Flag 只需要在代碼當中埋藏的一個變數或環境變數,雖然簡單可行但很難管理與追蹤,更沒辦法靈活快速的切換應對複雜突發情況。

const isJira1234FeatureFlagOn = import.meta.env.VITE_Jira_1234
if (isJira1234FeatureFlagOn) {
// Flag on
} else {
// Flag off
}

什麼是 OpenFeature?

OpenFeature🔗 是開源且支援多種供應商的開放標準,用於實踐通用的 Feature Flag:

Cloud

Your App

feature-flagging client

OpenFeature SDK

eval. context

eval. context

flag eval.

flag evaluation API

flag eval.

OpenFeature Provider

Feature Flag Service

OpenFeature 詞彙表

  • Evaluation API🔗
    • 允許開發者評估 Feature Flag,並使用評估結果來影響控制流程或功能。
  • Evaluation Context🔗
    • 一個容器用於存放任意上下文數據(如:user_id, region, email),這些數據可用作動態評估的基礎。
  • Providers🔗
    • 負責將提供給 Evaluation API 的參數對應到其在關聯標誌管理系統中的等效表示形式。提供者可以封裝供應商 SDK、呼叫自訂的標誌評估 REST API,甚至解析本機儲存的檔案來解析標誌值。
  • Hooks🔗
    • 用於在生命週期的各個階段添加任意行為。
  • Events🔗
    • 當系統觸發某些特定事件(如:Provider 已就緒、旗標設定變更、Provider 出現故障)時觸發。這讓應用程式能對環境變化做出即時反應。

如何使用 OpenFeature?

使用 OpenFeature 的過程就像是「配置插座」與「接入插頭」的過程,主要分為三個步驟:

一、配置 Provider(設定插座)

main.js
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-servicebilling)獲取不同的 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:

Backend
// Node.js/Express
app.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);
});
Frontend
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 provider
import { FlagdWebProvider } from '@openfeature/flagd-web-provider';
const provider = new FlagdWebProvider({
host: 'https://your-flagd-server.com',
port: 8013
});
await OpenFeature.setProviderAndWait(provider);

延伸閱讀