Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

介绍 Flagship:为 AI 时代构建的 feature flag

原文:Introducing Flagship: feature flags built for the age of AI Source: https://blog.cloudflare.com/flagship/

2026-04-17

AI 写代码比以往任何时候都多。AI 辅助的贡献现在占平台上新代码的快速增长份额。OpenCode 和 Claude Code 这样的 agentic 编码工具正在几分钟内交付整个功能。

AI 生成的代码进入生产只会加速。但更大的转变不仅是速度——而是自主性。

今天,一个 AI agent 编写代码,人类审阅、合并并部署它。明天,agent 自己做所有这些。问题变成了:你怎么让一个 agent 在不移除每个安全网的情况下交付到生产?

Feature flag 就是答案。Agent 在 flag 后面写一条新的代码路径并部署它——flag 关闭,所以对用户什么都没改变。然后 agent 为自己或一小撮测试群体启用 flag,在生产中练习这个功能,并观察结果。如果指标看起来不错,它就提升 rollout。如果什么东西坏了,它就禁用 flag。人类不需要为每一步都参与——他们设定边界,flag 控制爆炸半径。

这是 feature flag 一直在朝其建造的工作流程:不仅仅是将部署与发布解耦,而是将人类注意力与交付过程的每个阶段解耦。Agent 移动得快,因为 flag 让快速移动变得安全。

今天,我们宣布 Flagship——Cloudflare 原生的 feature flag 服务,基于 OpenFeature(用于 feature flag evaluation 的 CNCF 开放标准)构建。它在所有地方工作——Workers、Node.js、Bun、Deno 和浏览器——但在 Workers 上最快,因为 flag 在 Cloudflare 网络内 evaluate。使用 Flagship binding 和 OpenFeature,集成是这样的:

await OpenFeature.setProviderAndWait(
    new FlagshipServerProvider({ binding: env.FLAGS })
);

Flagship 现已 closed beta 提供。

Workers 上 feature flag 的问题

许多 Cloudflare 开发者已经诉诸于务实的变通方法:在他们的 Worker 中硬编码 flag 逻辑。说实话,在开始时它工作得足够好。Workers 在几秒钟内部署,所以在代码中翻转一个布尔值并推送到生产对于大多数情况已经足够快。

但它不会保持简单。一个硬编码的 flag 变成十个。十个变成五十个,由不同团队拥有,没有什么是开/关的中央视图。没有审计跟踪——当出问题时,你在 git blame 中搜索谁切换了什么。

对外部服务的网络调用

Worker 上使用的另一个常见模式是按以下方式向外部服务发起 HTTP 请求:

const response = await fetch("https://flags.example-service.com/v1/evaluate", {
      ...
      body: JSON.stringify({
        flagKey: "new-checkout-flow",
        context: {
          ...
        },
      }),
    });
const { value } = await response.json();
if (value === true) {
    return handleNewCheckout(request);
}
return handleLegacyCheckout(request);

那个出站请求位于每个用户请求的关键路径上。根据用户离 flag 服务的区域有多远,它可能增加可观的延迟。

这是一个奇怪的情况。你的应用在边缘运行,距用户毫秒。但 feature flag 检查迫使它在能决定渲染什么之前先穿越互联网到达另一个 API。

为什么本地 evaluation 不解决问题

一些 feature flag 服务提供“本地 evaluation“SDK。SDK 不会在每次请求时调用远程 API,而是将完整的 flag 规则集下载到内存中并在本地 evaluate。每次 evaluation 没有出站请求,flag 决策在进程内发生。

在 Workers 上,这些假设都不成立。没有长时存活的进程:一个 Worker isolate 可以被创建、服务一个请求,然后在一个请求和下一个请求之间被驱逐。一次新调用可能意味着从头重新初始化 SDK。

在 serverless 平台上,你需要一个已经在边缘的分发原语,缓存为你管理、读取是本地的、你不需要持久连接来保持东西最新。

Cloudflare KV 是一个很棒的原语来做这个!

Flagship 是如何工作的

Flagship 完全基于 Cloudflare 的基础设施构建——Workers、Durable Objects 和 KV。在 evaluation 路径中没有外部数据库、没有第三方服务、没有集中式 origin server。

当你创建或更新一个 flag 时,控制平面将更改原子地写入一个 Durable Object——一个 SQLite-backed 的、全局唯一的实例,作为该应用 flag 配置和变更日志的真相来源。在几秒内,更新的 flag 配置同步到 Workers KV(Cloudflare 的全球分发的键值存储),在那里它在 Cloudflare 网络上复制。

当请求 evaluate 一个 flag 时,Flagship 直接在边缘从 KV 读取 flag 配置——已经在处理请求的同一个 Cloudflare 位置。Evaluation 引擎然后就在那里的一个 isolate 中运行:它将请求上下文与 flag 的 targeting 规则匹配,解析 rollout 百分比,并返回一个变体。数据和逻辑都在边缘——什么都不发到别处去 evaluate。

使用 Flagship:Worker binding

对于运行 Cloudflare Workers 的团队,Flagship 提供一个直接的 binding,在 Workers 运行时内 evaluate flag——没有 HTTP 往返,没有 SDK 开销。把 binding 添加到你的 wrangler.jsonc,你的 Worker 就连接好了:

{
  "flagship": [
    {
      "binding": "FLAGS",
      "app_id": "<APP_ID>"
    }
  ]
}

就这样。你的账号 ID 从 Cloudflare 账号推断,app_id 将 binding 与特定的 Flagship app 绑定。在你的 Worker 中,你只是请求 flag 值:

export default {
  async fetch(request: Request, env: Env) {
    // Simple boolean check
    const showNewUI = await env.FLAGS.getBooleanValue('new-ui', false, {
      userId: 'user-42',
      plan: 'enterprise',
    });
    // Full evaluation details when you need them
    const details = await env.FLAGS.getStringDetails('checkout-flow', 'v1', {
      userId: 'user-42',
    });
    // details.value = "v2", details.variant = "new", details.reason = "TARGETING_MATCH"
  },
};

Binding 支持每种变体类型的类型化访问器——getBooleanValue()getStringValue()getNumberValue()getObjectValue()——加上 *Details() 变体,它们返回解析后的值连同匹配的变体和它被选中的原因。在 evaluation 错误时,默认值被优雅地返回。在类型不匹配时,binding 抛出一个异常——那是你代码中的 bug,不是临时性故障。

SDK:OpenFeature 原生

大多数 feature flag SDK 带有自己的接口和 evaluation 模式。随着时间推移,这些深深嵌入到你的代码库中——切换提供商意味着重写每个调用站点。

我们不想构建另一个那样的东西。Flagship 基于 OpenFeature(feature flag evaluation 的 CNCF 开放标准)构建。OpenFeature 定义了跨语言和提供商的 flag evaluation 公共接口——这是 OpenTelemetry 与可观察性所具有的相同关系。你针对标准只编写一次 evaluation 代码,通过更改单行配置来切换提供商。

import { OpenFeature } from '@openfeature/server-sdk';
import { FlagshipServerProvider } from '@cloudflare/flagship/server';
await OpenFeature.setProviderAndWait(
  new FlagshipServerProvider({
    appId: 'your-app-id',
    accountId: 'your-account-id',
    authToken: 'your-cloudflare-api-token',
  })
);
const client = OpenFeature.getClient();
const showNewCheckout = await client.getBooleanValue(
  'new-checkout-flow',
  false,
  {
    targetingKey: 'user-42',
    plan: 'enterprise',
    country: 'US',
  }
);

如果你在 Workers 上运行并使用 Flagship binding,你可以直接将其传给 OpenFeature provider。Binding 已经携带你的账号上下文,所以没什么要配置——认证是隐式的。

import { OpenFeature } from '@openfeature/server-sdk';
import { FlagshipProvider } from '@cloudflare/flagship/server';
let initialized = false;
export default {
  async fetch(request: Request, env: Env) {
    if (!initialized) {
      await OpenFeature.setProviderAndWait(
        new FlagshipServerProvider({ binding: env.FLAGS })
      );
      initialized = true;
    }
    const client = OpenFeature.getClient();
    const showNewCheckout = await client.getBooleanValue('new-checkout-flow', false, {
      targetingKey: 'user-42',
      plan: 'enterprise',
    });
  },
};

你的 evaluation 代码不变——OpenFeature 接口是相同的。但在底层,Flagship 通过 binding 而不是 HTTP 来 evaluate flag。你获得标准的可移植性以及 binding 的性能。

也有一个客户端 provider 可用于浏览器。它预取你指定的 flag,用可配置的 TTL 缓存它们,并从该缓存同步地服务 evaluation。

你能用 Flagship 做什么

Flagship 支持你期望从一个 feature flag 服务获得的模式,以及当 AI 生成的代码每天进入生产时变得至关重要的模式。

Flag 值可以是布尔、字符串、数字或完整的 JSON 对象——对于配置块、UI 主题定义,或将用户路由到不同的 API 版本而不维护单独的代码路径很有用。

Targeting Rules

每个 flag 可以有多个规则,按优先级顺序 evaluate。第一个匹配的规则获胜。

一个规则由以下组成:

  • 决定该规则是否适用于给定上下文的条件

  • 当规则匹配时要服务的 flag 变体

  • 一个可选的 rollout,用于基于百分比的传递

  • 一个优先级,决定多个规则存在时的 evaluation 顺序(更低的数字 = 更高的优先级)

嵌套逻辑条件

条件可以使用 AND/OR 逻辑组合,嵌套最多五层深。一个规则可以表达像这样的事情:

(plan == "enterprise" AND region == "us" ) OR (user.email.endsWith("@cloudflare.com"))
= serve ("premium")

在规则的顶层,多个条件用隐式 AND 组合,所有条件必须通过规则才匹配。在每个条件内部,你可以为更复杂的逻辑嵌套 AND/OR 组。

按百分比 Flag Rollout

不像渐进式部署(在你 Worker 的不同上传版本之间分割流量),feature flag 让你在服务 100% 流量的单个版本内按百分比 rollout 行为。

任何规则都可以包括百分比 rollout。不是给所有匹配条件的人服务一个变体,而是给其中一个百分比的人服务它。

Rollout 在指定的上下文属性上使用一致性 hashing。相同的属性值(例如 userId)总是 hash 到相同的桶,所以他们不会在请求之间在变体之间翻转。你可以从 5% 提升到 10% 到 50% 到 100% 的用户,这样那些已经在 rollout 中的人保持在其中。

为接下来的事情而生

AI 生成的代码进入生产只会加速。Agentic 工作流将进一步推动它——agent 自主地在生产中部署、测试和迭代代码。在这个世界里茁壮成长的团队不会是交付最快的那些。他们将是那些可以快速交付并仍然保持对用户看到什么的控制、在出问题时几秒内回滚,并自信地逐步暴露新代码路径的那些。

这就是 Flagship 为之而生的:

  • 跨整个地球的 evaluation,使用 K/V 全球缓存。

  • 完整的审计跟踪。 每个 flag 变更都用字段级别的 diff 记录,所以你知道谁在什么时候改了什么。

  • Dashboard 集成。团队中的任何人都可以切换 flag 或调整 rollout 而不接触代码。

  • OpenFeature 兼容。 不重写你的 evaluation 代码就采用 Flagship。离开时也不需要重写它。

开始使用 Flagship

从今天起,Flagship 处于 private beta。你可以在这里申请访问。我们将在接近正式发布时分享更多关于定价的细节。

  • 访问 Cloudflare dashboard 创建你的第一个 Flagship app

  • 安装 SDK:npm i @cloudflare/flagship;或在你的 Worker 中直接使用 Worker binding

  • 阅读文档查看集成指南和 API 参考

  • 查看源代码查看示例并贡献

如果你目前在你的 Worker 中硬编码 flag,或通过给每个请求增加延迟的外部服务 evaluate flag,试试 Flagship。我们想听听你构建什么。