AI 正在以前所未有的速度向生产环境输出代码。当 Agent 可以自主部署、测试、迭代时,你还能保持对用户看到内容的控制吗?
一、时代背景:AI 写代码已不是新鲜事
AI 辅助贡献的代码在各平台新代码中占比正在快速增长,OpenCode、Claude Code 等 Agentic 编程工具能在几分钟内完成一个完整功能的交付。
AI 生成代码进入生产环境的速度只会越来越快,但更大的转变不只是速度------而是自主性。今天,AI Agent 写代码、人类审查并部署;明天,Agent 将全程自主完成。
这就带来一个关键问题:如何让 Agent 快速部署,同时不拆掉所有安全网?
答案是 Feature Flag(功能开关)。Agent 在一个 Flag 后面写好新的代码路径并部署------Flag 处于关闭状态,用户不受任何影响。然后 Agent 为自己或少量测试用户打开 Flag,在生产环境中验证功能,观察结果。如果指标良好就逐步扩大灰度,出了问题就关闭 Flag。整个过程人类不需要介入每一步------只需设定边界,Flag 来控制影响范围。
这正是 Feature Flag 一直以来的终极形态:不只是把部署与发布解耦,而是把人类注意力从每一个发布环节中解放出来。
二、传统方案的三个痛点
痛点一:硬编码 Flag,越堆越乱
很多 Cloudflare 开发者的务实做法是把 Flag 逻辑直接硬编码进 Worker。这在初期确实够用------Worker 秒级部署,改个布尔值推上去就完了。但问题是,它不会一直简单下去。一个硬编码的 Flag 会变成十个,十个变成五十个,归属于不同团队,没有统一视图可以看到哪些开关是开着的、哪些是关着的。出了问题,只能翻 git blame 去查是谁改了什么。
痛点二:HTTP 调用外部服务,延迟无法接受
另一种常见方式是在 Worker 里发起 HTTP 请求,调用外部的 Feature Flag 服务。这个请求就落在每个用户请求的关键路径上------根据用户与 Flag 服务所在区域的距离不同,可能引入相当可观的延迟。
说白了就是:你的应用跑在离用户只有几毫秒的边缘节点,但每次做一个功能决策,都要往回绕半个地球去问一次。
痛点三:本地评估 SDK 在 Serverless 上根本行不通
有些 Feature Flag 服务提供"本地评估"SDK,把完整的 Flag 规则下载到内存里在本地计算,不需要每次请求都发 HTTP。但这个方案在 Workers 上根本站不住脚------Workers 没有持久进程,一个 Worker 隔离实例可能在处理完一次请求后就被驱逐,下次请求来了可能又要从零重新初始化 SDK。
在无服务器平台上,你需要的是一个本身就在边缘的分发原语------缓存由平台托管,读取是本地的,也不需要持久连接来保持数据同步。Cloudflare KV 就是这样一个很好的基础能力。
三、Flagship 的架构设计
Flagship 完全构建在 Cloudflare 自身基础设施之上------Workers、Durable Objects 和 KV。评估路径中没有外部数据库、没有第三方服务、没有中心化的源站服务器。
当你创建或更新一个 Flag,控制平面会原子写入到一个 Durable Object------这是一个以 SQLite 为后端的全局唯一实例,作为该应用 Flag 配置和变更日志的唯一数据源。几秒内,更新后的 Flag 配置会同步到 Workers KV,并在 Cloudflare 全球网络中完成复制。
当请求需要评估一个 Flag 时,Flagship 直接从边缘的 KV 读取配置------就是已经在处理这个请求的那个 Cloudflare 节点。评估引擎就在当地的 isolate 里运行:将请求上下文与 Flag 的 Targeting 规则匹配,解析灰度比例,返回变体结果。数据和逻辑都在边缘------没有任何东西需要发送到别处。
整个链路可以概括为:Flag 的数据就在你代码运行的地方,评估在本地完成,零网络开销。
四、如何使用 Flagship
Worker Binding:最简集成方式
对于 Cloudflare Workers 用户,只需在 wrangler.jsonc 里加一段配置:
json
{
"flagship": [
{
"binding": "FLAGS",
"app_id": "<APP_ID>"
}
]
}
在 Worker 里就可以直接调用:
typescript
const showNewUI = await env.FLAGS.getBooleanValue('new-ui', false, {
userId: 'user-42',
plan: 'enterprise',
});
Binding 支持所有类型的取值方法------getBooleanValue()、getStringValue()、getNumberValue()、getObjectValue(),以及带 *Details() 后缀的变体,会同时返回匹配到的变体名称和命中原因。评估出错时返回默认值;类型不匹配时抛出异常------因为那属于代码层面的 bug,而不是瞬态故障。
OpenFeature:不绑定厂商的开放标准
大多数 Feature Flag SDK 都自带一套接口和评估模式,时间久了会深嵌进你的代码库------切换 Provider 意味着重写每一处调用点。Flagship 基于 OpenFeature 构建,这是 CNCF 的功能开关评估开放标准。你按照标准写好评估代码,换 Provider 只需改一行配置------就像 OpenTelemetry 之于可观测性一样。
五、核心功能一览
灵活的 Targeting 规则
每个 Flag 可以配置多条规则,按优先级依次评估,第一条命中的规则生效。规则由条件、对应的变体、可选的灰度比例以及优先级数字组成。
条件支持 AND/OR 嵌套逻辑,最多五层。一条规则可以表达这样的复杂逻辑:
ruby
(plan == "enterprise" AND region == "us") OR (user.email.endsWith("@cloudflare.com"))
=> 返回 "premium" 变体
百分比灰度发布
任意规则都可以附带百分比灰度配置。对于命中条件的用户,只向其中一定比例提供该变体。灰度使用对指定上下文属性的一致哈希,同一属性值(比如 userId)永远落在同一个桶里,不会在请求之间来回跳变。你可以从 5% 逐步推到 10%、50%、100%,已进入灰度的用户会持续留在其中。
完整审计日志
每一次 Flag 变更都会被记录,包含字段级别的 diff,清楚呈现谁在什么时间改了什么内容。
六、适合哪些人用
如果你满足以下任一情况,Flagship 值得认真考虑:
- 正在 Worker 里硬编码 Feature Flag,规模开始失控
- 依赖外部 Feature Flag 服务,每次评估都有额外延迟
- 团队在用 AI 工具加速交付,需要更安全的灰度和回滚机制
- 希望避免深度绑定某家 Feature Flag 厂商的私有接口
七、总结
Flagship 本质上回答的是一个很朴素的问题:当代码越跑越快、越来越自动化时,如何保证你依然对用户体验有掌控权?
能在这个世界里胜出的团队,不是发货最快的,而是能快速发货、出了问题秒级回滚、新代码路径有信心地逐步开放------同时依然清楚地知道用户在看什么的团队。
目前 Flagship 处于私有测试阶段,感兴趣可以前往 Cloudflare 官网申请早期访问权限。