
特性标志与渐进式交付
为什么使用特性标志?
无标志:部署 -> 所有用户立即获得新功能
有标志:部署 -> 按用户/分段控制发布
使用场景:
- 金丝雀发布(0% -> 1% -> 10% -> 50% -> 100%)
- A/B 测试(在全面发布前衡量影响)
- 熔断开关(无需重新部署即可禁用有问题的功能)
- 针对特定用户/公司的 Beta 功能
- 暗启动(发布代码,稍后启用)

简单实现
interface FeatureFlag {
enabled: boolean;
percentage?: number;
userIds?: string[];
segments?: string[];
}
class FeatureFlagService {
private flags = new Map<string, FeatureFlag>();
constructor(private redis: RedisClient) {}
async isEnabled(flagKey: string, user: { id: string; segment?: string }): Promise<boolean> {
const flag = await this.getFlag(flagKey);
if (!flag) return false;
if (!flag.enabled) return false;
// 特定用户覆盖
if (flag.userIds?.includes(user.id)) return true;
// 基于分段
if (flag.segments && user.segment && flag.segments.includes(user.segment)) return true;
// 百分比发布(每个用户一致)
if (flag.percentage !== undefined) {
const hash = parseInt(md5(`${flagKey}:${user.id}`).substring(0, 8), 16);
return (hash % 100) < flag.percentage;
}
return flag.enabled;
}
private async getFlag(key: string): Promise<FeatureFlag | null> {
const cached = await this.redis.get(`flag:${key}`);
if (cached) return JSON.parse(cached);
const flag = await this.db.findFlag(key);
if (flag) await this.redis.setEx(`flag:${key}`, 30, JSON.stringify(flag));
return flag;
}
}
// 在代码中使用
const featureFlags = new FeatureFlagService(redis);
app.get('/checkout', async (req, res) => {
const newCheckout = await featureFlags.isEnabled('new-checkout-flow', req.user);
if (newCheckout) {
return newCheckoutController.handle(req, res);
}
return legacyCheckoutController.handle(req, res);
});

OpenFeature 标准
// OpenFeature - 供应商中立的特性标志标准
import { OpenFeature } from '@openfeature/server-sdk';
import { LaunchDarklyProvider } from '@openfeature/launchdarkly-provider';
await OpenFeature.setProvider(new LaunchDarklyProvider(process.env.LD_SDK_KEY!));
const client = OpenFeature.getClient('my-app');
// 评估上下文
const context = { targetingKey: user.id, email: user.email, plan: user.plan };
// 布尔标志
const newDashboard = await client.getBooleanValue('new-dashboard', false, context);
// 字符串标志(多变量)
const checkoutVariant = await client.getStringValue('checkout-variant', 'control', context);
// 数字标志
const maxItems = await client.getNumberValue('cart-max-items', 10, context);

金丝雀发布流程
// 渐进式发布
async function progressiveRollout(flagKey: string): Promise<void> {
const stages = [1, 5, 10, 25, 50, 100];
for (const percentage of stages) {
await flagService.update(flagKey, { percentage });
console.log(`已将 ${flagKey} 发布到 ${percentage}%`);
// 监控 10 分钟
await waitAndMonitor(10 * 60 * 1000, {
errorRateThreshold: 0.01, // 1% 错误率
latencyP99Threshold: 500, // 500ms P99
onThresholdExceeded: async () => {
await flagService.update(flagKey, { percentage: 0, enabled: false });
throw new Error(`在 ${percentage}% 时触发回滚`);
}
});
}
}
特性标志将部署与发布解耦——随时发布代码,安全地发布功能。