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 的参数对应到其在关联标志管理系统中的等效表示形式。Provider 可以封装供应商 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);

延伸阅读