Skip to content
源码分析手册

Feature Flags & GrowthBook:云端下发的远程智能与实验开关

本质

Claude Code 作为一个 CLI 工具,其二进制文件的分发和更新周期相对较慢。为了实现新特性的灰度发布、紧急故障的实时熔断(Kill Switch)以及 A/B 测试,系统集成了一套基于 GrowthBook 平台的远程功能配置(Feature Flags)方案。

这套系统的本质是一个“云端配置中心”。它允许 Anthropic 团队在无需发布新版本的情况下,精准控制不同用户群体(如内部员工、Pro 用户、企业组织)看到的界面、可用的工具以及底层的模型调度逻辑。它既是特性的开关,也是 CLI 实时感知的“远程智能”来源。

运行时的真相

这套机制的核心逻辑位于 claude-code-opensource/src/services/analytics/growthbook.ts

初始化与加载流程

在 CLI 启动时,initializeGrowthBook() 会被调用。它会尝试从远程端点(默认为 api.anthropic.com)拉取最新的功能定义。为了平衡实时性与启动速度,系统设计了精妙的加载逻辑:

  1. 超时保护:网络请求的超时时间被严格限制在 5 秒。
  2. 本地磁盘缓存:如果请求超时或处于离线状态,系统会退而求其次,读取 GlobalConfig 中存储的 cachedGrowthBookFeatures
  3. 内存热替换:一旦远程获取成功,processRemoteEvalPayload() 会解析返回的数据。由于 SDK 对 API 返回格式的预期差异,源码中还包含了一层转换逻辑(将 value 映射到 defaultValue),随后将最新的配置同步回内存中的 remoteEvalFeatureValues 并持久化到磁盘。

定位属性(Targeting Attributes)

为了实现精准投放,CLI 会向 GrowthBook 发送一系列用户属性(Attributes),包括但不限于:匿名化的 deviceId、当前的 sessionId、操作系统平台、用户的 organizationUUID 以及 subscriptionType(订阅类型)。这些属性在本地组合,并作为远程评估(Remote Evaluation)的上下文。

刷新与重置机制

远程配置并非一成不变。系统会根据用户类型设定不同的定期刷新频率:普通用户每 6 小时刷新一次,而内部开发人员("Ants")则是每 20 分钟刷新一次。此外,当用户执行登录、退出或切换组织的操作时,由于 GrowthBook 客户端的 HTTP 请求头(Auth Headers)是不可变的,系统必须通过 refreshGrowthBookAfterAuthChange() 彻底销毁并重建客户端,以确保功能开关与当前的身份状态严格同步。

覆盖与调试(Overrides)

为了方便测试,系统提供了两级覆盖机制:

  • 环境变量覆盖:内部用户可以设置 CLAUDE_INTERNAL_FC_OVERRIDES 环境变量,传入一个 JSON 对象来强制指定某些功能的值。
  • 持久化覆盖:在 /config 菜单的 Gates 选项卡中手动修改的值会被存储在 GlobalConfig.growthBookOverrides 中,这些值的优先级高于服务器下发的值。

边界条件

首先,“缓存优先”与“阻塞初始化”的选择。大部分 UI 渲染和普通功能使用 getFeatureValue_CACHED_MAY_BE_STALE,它追求瞬时响应,即使数据可能是上个会话的旧值。而对于涉及计费、安全或模型选型的关键逻辑,必须使用 getFeatureValue_DEPRECATED(虽带 Deprecated 标签,但其实指代“阻塞初始化”),它会等待网络请求完成,确保拿到最准确的权限定义。

其次,离线状态下的行为。当完全断网时,所有功能将回退到上次在线时缓存的状态。如果你刚在云端控制台关掉了一个特性,但 CLI 还没来得及同步且此时断网,该特性在 CLI 端可能会继续运行直到下次同步成功。

第三,身份绑定的滞后性。虽然有刷新机制,但在极端情况下,刚购买了订阅的用户可能需要重启 CLI 或等待一个刷新周期,才能通过 GrowthBook 感知到“已订阅”状态的变化。

第四,曝光日志(Exposure Logging)。为了统计实验结果,每当你访问一个被标记为实验(Experiment)的功能时,系统会自动记录一次曝光。这种记录在单次会话内是去重的,避免频繁调用对性能的影响。

推荐阅读路径

如果你想了解这些功能开关是如何持久化到磁盘的,请参阅 Global Config vs. State

如果你想追踪功能开关对数据采集的影响,可以深入分析 Analytics Pipeline

如果你对内部开发者的特殊调试权限感兴趣,可以查看 src/utils/user.ts 中关于 USER_TYPE 的定义。

源码锚点

  • claude-code-opensource/src/services/analytics/growthbook.ts: 系统的中枢,负责客户端生命周期、远程评估与刷新逻辑。
📄 src/services/analytics/growthbook.ts — 系统的中枢,负责客户端生命周期、远程评估与刷新逻辑。L20-23 of 1156
typescript
  type GitHubActionsMetadata,
  getUserForGrowthBook,
} from '../../utils/user.js'
import {
  • claude-code-opensource/src/utils/config.ts: 提供了 feature() 包装函数以及配置持久化的接口。
📄 src/utils/config.ts — 提供了 `feature()` 包装函数以及配置持久化的接口。L36-44 of 1818
typescript
const teamMemPaths = feature('TEAMMEM')
  ? (require('../memdir/teamMemPaths.js') as typeof import('../memdir/teamMemPaths.js'))
  : null
const ccrAutoConnect = feature('CCR_AUTO_CONNECT')
  ? (require('../bridge/bridgeEnabled.js') as typeof import('../bridge/bridgeEnabled.js'))
  : null

/* eslint-enable @typescript-eslint/no-require-imports */
import type { ImageDimensions } from './imageResizer.js'
  • claude-code-opensource/src/utils/user.ts: 定义了发送给 GrowthBook 的用户定位属性。
📄 src/utils/user.ts — 定义了发送给 GrowthBook 的用户定位属性。L32-47 of 195
typescript
 * This is also the format used by GrowthBook.
 */
export type CoreUserData = {
  deviceId: string
  sessionId: string
  email?: string
  appVersion: string
  platform: typeof env.platform
  organizationUuid?: string
  accountUuid?: string
  userType?: string
  subscriptionType?: string
  rateLimitTier?: string
  firstTokenTime?: number
  githubActionsMetadata?: GitHubActionsMetadata
}
  • claude-code-opensource/src/constants/keys.ts: 存储 GrowthBook 客户端的 SDK Key。
📄 src/constants/keys.ts — 存储 GrowthBook 客户端的 SDK Key。L5-11 of 12
typescript
export function getGrowthBookClientKey(): string {
  return process.env.USER_TYPE === 'ant'
    ? isEnvTruthy(process.env.ENABLE_GROWTHBOOK_DEV)
      ? 'sdk-yZQvlplybuXjYh6L'
      : 'sdk-xRVcrliHIlrg4og4'
    : 'sdk-zAZezfDKGoZuXXKe'
}

基于 Claude Code v2.1.88 开源快照的深度分析