您已经完成了该功能。拉取请求是干净的。QA说它看起来很好。但是您仍然不想一次性将其部署到所有人身上。
通常,这种感觉是您的React应用已经超出了简单部署的迹象。一旦产品有了真正的用户,发布就不再仅仅是一个技术事件了。它变成了一个风险决策。如果新的搜索UI破坏了它,或者如果checkout变体混淆了用户,或者如果一个移动构建部署了code您无法快速撤销,您就需要更多的东西。 if (process.env.NODE_ENV) 而不是
react特性标志 开始发挥作用。不是作为一个可爱的布尔值在组件中,而是作为一个发布控制层,让您可以单独部署__CAPGO_KEEP_0__而不暴露它。在Web应用中,这意味着更安全的发布。在像__CAPGO_KEEP_1__或Electron这样的打包应用中,它甚至更重要,因为回滚速度受到商店审查、安装延迟和更慢的发布周期的限制。 start to matter. Not as a cute boolean in a component, but as a release control layer that lets you ship code separately from exposing it. In web apps, that means safer rollouts. In bundled apps like Capacitor or Electron, it matters even more because rollback speed is limited by store review, install lag, and slower release cycles.
上下文:Capgo营销网站。角色:短的UI标签或导航项。见于:页面blog/[slug].astro。消息键`table_of_contents` (目录)。
- 为什么特性标志对于现代React应用至关重要
- 在您的React应用中架构特性标志
- 实施回滚和回滚策略
- 测试可观察性和管理标志债务
- 保护您的标志并使用 CI/CD 自动化
- 超越Web特性标志:Capacitor和移动应用
为什么特性标志对于现代React应用至关重要
星期五下午发布。新账单摘要UI已经部署,支持团队已经打开了发布清单,一个企业客户仍然需要使用旧的流程直到星期一。在Web应用中,这已经很紧张。在通过桌面安装程序或移动应用商店发布的捆绑React应用中,情况会更糟糕,因为回滚可能需要几个小时甚至几天,而不是几分钟。
特性标志让React团队控制这一时刻。它们让你可以发布code,让它保持休眠状态,并决定哪些用户应该看到它。这样改变了发布工作从全部或无关紧要的事件转变为一个受控的操作。

部署和发布是不同的工作
部署回答,“code是否已经发布?”发布回答,“谁可以现在执行这个行为?”
当 React 应用程序有实时流量、多个环境和影响收入、权限或导航的功能时,这一区别就变得很重要。团队可以在内部小组中测试,在生产环境中测试,并在他们信任行为后才扩大访问权限。对于像 Capacitor 应用程序、Electron 应用程序和经过商店审查的移动构建这样的较慢发布平台,这种控制就更加宝贵,因为二进制文件可能已经在用户的手中了,而功能还没有准备好供所有人使用。
旗帜在以下三个常见情况下很有帮助:
- 控制发布: 首先暴露给一个小组
- 实验: 比较变体而不需要维护单独的部署
- 快速关闭: 禁用一个风险功能而不等待新构建
一个简单的规则在这里很有效。如果生产问题需要花费大量时间来逆转,code 就应该在旗帜后面发布。
新到旗帜的团队通常会停留在 UI 条件化上。 flag ? <NewUI /> : <OldUI /> 这是可见的部分,但这不是最有趣的部分。其核心价值是运营性的。远程配置、确定性目标和快速关闭功能是旗帜在生产环境中有用的关键。 如果您的 React 应用程序还需要应用级的运行时设置,则需要一个 remote config plugin for Capacitor apps 适合同一发布控制模型。
标志在没有人信任它们时就不再有用。
我在不断增长的前端代码库中看到相同的失败模式。团队快速添加标志,环境之间的名称漂移,回退值隐藏配置错误, nobody 确定“开”是否意味着全球开启,仅限员工开启,还是仅在测试环境开启。在这种情况下,标志系统开始创造风险,而不是减少风险。
类型安全有所帮助,但它并不能解决整个问题。团队仍然需要一个清晰的注册表,拥有者和一致的方式来评估标志跨应用。否则,React 组件最终会在发布或部分回滚时打破本地假设。
区别很容易看出:
| 用例 | 弱版本 | 强版本 |
|---|---|---|
| UI 切换 | 组件本地布尔值 | 拥有者和发布规则的远程标志 |
| 发布安全 | 手动部署回滚 | 通过远程配置立即禁用 |
| 实验 | 临时分支比较 | 稳定分组分配和可测量的曝露 |
重要的思维转变很简单。 React 特性标志属于您的发布过程,而不仅仅是您的 JSX。 尤其是在部署新版本的速度很慢的应用程序中,处理它们时,标志成为减少生产环境混乱时爆炸半径的少数工具。
在 React 应用程序中架构特性标志
架构决策比第一个标志更重要。如果您直接将标志连接到随机组件中,会出现重复的逻辑、加载闪烁和无法信任的源代码的代码库。
使用运行时提供者,而不是散布的条件语句
对于 React 应用程序,可靠的方法是将标志视为 运行时数据. Guidance for React flagging recommends three things: evaluate flags on the server or in a local SDK cache, persist cohort assignment deterministically, and render the final UI state before hydration or use anti-flicker protection so users don’t see the wrong default first (React 标志方法论).
这会影响你的 code 应该存放的位置。将标志加载放在应用程序根部。使其简单化。避免在叶子组件内部获取标志。
实践形状如下:
- 在主树渲染之前加载或注入标志。
- 通过提供者暴露它们。
- 通过一个钩子或一个包装模式读取它们。
- 将评估逻辑保留在展示组件之外。
如果您需要一个远程配置层来管理应用程序级别的设置以及标志,则一个工具,如 __CAPGO_KEEP_0__ 远程配置插件 Capacitor remote config plugin 模式一:使用 React Context 和一个自定义钩子
这是我通常推荐的默认模式。它是明确的、可测试的,并且如果您切换供应商后可以轻松迁移。
模式二:使用 __CAPGO_KEEP_0__ 远程配置插件
import React, { createContext, useContext, useMemo } from 'react';
type FlagValue = boolean | 'control' | 'variant-a' | 'variant-b';
type Flags = {
newCheckout: boolean;
checkoutExperiment: FlagValue;
deleteTaskEnabled: boolean;
};
const defaultFlags: Flags = {
newCheckout: false,
checkoutExperiment: 'control',
deleteTaskEnabled: false,
};
const FeatureFlagContext = createContext<Flags>(defaultFlags);
export function FeatureFlagProvider({
flags,
children,
}: {
flags: Flags;
children: React.ReactNode;
}) {
const value = useMemo(() => flags, [flags]);
return (
<FeatureFlagContext.Provider value={value}>
{children}
</FeatureFlagContext.Provider>
);
}
export function useFeatureFlag<K extends keyof Flags>(key: K): Flags[K] {
return useContext(FeatureFlagContext)[key];
}
使用方式保持平淡,这正是你想要的:
function DeleteTaskButton() {
const enabled = useFeatureFlag('deleteTaskEnabled');
if (!enabled) return null;
return <button>Delete task</button>;
}
这种模式很好用,因为你的组件只会问一个最终答案。它们不关心答案是如何计算出来的。
第二种模式:使用高阶组件
A 高阶组件 在你想要在整个屏幕、路由元素或遗留类组件上设置门控时,高阶组件是有用的,而不必在每个地方添加hook调用。
import React from 'react';
import { useFeatureFlag } from './FeatureFlagProvider';
export function withFeatureFlag<P>(
flagKey: 'newCheckout' | 'deleteTaskEnabled',
Fallback?: React.ComponentType<P>
) {
return function wrap(Component: React.ComponentType<P>) {
return function FeatureFlaggedComponent(props: P) {
const enabled = useFeatureFlag(flagKey);
if (!enabled) {
return Fallback ? <Fallback {...props} /> : null;
}
return <Component {...props} />;
};
};
}
使用方式:
const CheckoutPage = () => <div>New checkout</div>;
const LegacyCheckoutPage = () => <div>Legacy checkout</div>;
export default withFeatureFlag('newCheckout', LegacyCheckoutPage)(CheckoutPage);
缺点是间接性。现代React中的hook更容易跟踪,而HOC在DevTools中的组件树可能会变得更杂乱。然而,对于路由级门控,它们仍然是干净的。
不要让组件决定发布策略。组件应该消费一个标志结果,而不是实现分桶、用户目标或缓存刷新规则。
React特性标志模式的比较
| 标准 | 上下文+hook | 高阶组件 (HOC) |
|---|---|---|
| 最佳使用场景 | 组件级别的决策和变体 | 包裹整个页面、路由或遗留组件 |
| 灵活性 | 高 | 中 |
| 开发者体验 | 在现代函数组件中很强大 | 当钩子不方便使用时很有用 |
| 包裹清晰度 | 清晰的导入和直接读取 | 抽象化在树中更高 |
| 测试 | 通过提供商轻松模拟 | 包装的整合案例更容易 |
| 长期可维护性 | 通常更好 | 当使用时谨慎 |
如果您第一次实现React特性标志,请从 上下文+钩子开始。只有当您有特定的需要时才添加HOC。
实施回滚和回滚策略
回滚计划在功能发布后出现问题的那一天最为重要。UI可能只显示一个新按钮或屏幕,但关键任务是决定谁先看到它,暴露速度如何以及如何快速关闭它而不必等待重新部署。尤其是在React应用程序被打包在移动或桌面应用程序中时,这更为重要,因为回滚取决于远程配置,因为应用商店审查或桌面分发需要时间

百分比滚动需要粘性分配
百分比滚动只有当分配是稳定的时才会生效。如果同一用户在一次访问中得到新版的支付页面,而在下一次访问中得到旧版的支付页面,支持团队就无法复制问题,分析数据会变得杂乱无章,用户也会失去信任。
解决方法很简单。使用一个稳定的标识符和标志键的确定性哈希来分桶用户。用户ID通常是正确的输入。如果您没有用户ID,可以使用安装ID或设备ID。 Math.random() 在浏览器中使用的工具是错误的,因为它会不受控制地重新分配用户。
一个实际的滚动路径如下:
- 首先是内部用户和QA
- 然后是向小规模用户发布
- 然后是逐步扩大发布,检查错误率、转化率和支持票数
- 整个标志生命周期中都要保持分配的稳定性
最后一点容易低估。粘性分组不仅适用于实验。它们使事故响应更快,因为工程师可以立即回答一个基本问题:哪些用户暴露于风险中?
如果您运行实验,务必要在发布之前进行样本大小计算。Optimizely提供了一个样本大小计算器,展示了流量量、基准转化率和最小可检测效果如何影响每个变体所需的用户数量Optimizely样本大小计算器. 如果没有检查,团队经常会将噪音误认为是信号,并提前推广一个功能。
一个有用的参考资料,用于在浏览器外的阶段性更新 阶段性发布的Capacitor实时更新. 在React应用程序运行在打包的壳中并且二进制回滚较慢时,相同的发布纪律适用。
目标和环形发布减少了爆炸半径
有些功能不应以随机百分比开始。账单流程、权限提示、数据迁移以及任何可能锁定用户的功能通常需要目标发布。
目标发布在以下情况下效果良好:
- 内部员工进行内部测试
- 同意存在粗糙边缘的beta测试者
- 特定账户等级
- 具有不同法律或语言要求的地区
- 设备或应用版本支持该功能的安全性
环形发布使目标更具操作性。环 0 是员工。环 1 是信任的外部测试者。随着信心的提高,后续环的暴露范围会扩大。这一结构有助于团队避免将所有用户视为一个池,尽管风险明显不均衡。
以下是嵌入式教程,配套使用此发布模型:
杀死switch是旗帜的价值所在
每个有风险的功能都需要一个快速的脱离路径。在实践中,这通常意味着一个顶级的操作性旗帜,禁用整个功能流,而不是一个仅仅隐藏一个入口点的显示旗帜,后台请求、效果或导航路径仍然在运行。
在发布前设计杀死switch:
- 在应用启动时尽早评估它。
- 缓存最后一次安全值。
- 如果旗帜服务不可用时选择一个安全的默认值。
- 确保禁用功能停止副作用,而不是仅仅停止渲染。
- 记录谁可以在事故期间翻转它。
对于仅限Web应用,发布风险减少。对于移动和桌面React应用,它可以是区分轻微事故和等待用户获取修复版的差异。如果code已经在捆绑包中发布,远程旗帜就成为回滚策略的一部分,而不是仅仅是发布策略的一部分。
测试可观察性和管理标志债务
添加标志的容易部分是添加一个。添加标志的昂贵部分是后来,当有很多标志时,Nobody 记得哪些仍然重要。

每个标志都增加了您必须信任的状态数量
马丁福勒的警告仍然有效:一旦存在标志,团队必须验证 开启 关闭 状态, 和状态,).
并且,
- 当有多个标志时,可能的状态组合会以组合方式增长,这会提高回归风险(马丁福勒关于特性开关的说法) A个页面可以有多个branch在任何人注意之前。
- 水合不匹配变得更容易触发: 客户端和服务器如果评估发生在错误的时间,可能会产生分歧。
- 快照测试变得不那么有用: 一个happy-path渲染并不能告诉你太多,如果相反的flag状态没有测试的话。
一个实用的测试栈看起来像这样:
- 单元测试评估逻辑。
- 组件测试关键flag branch。
- 添加对有风险路径的端到端覆盖。
- 明确验证默认fallback。
不要追求每种combination。通常会在自己的重量下崩溃。测试那些可能伤害用户或破坏布局的状态。
flag债务是真实的,它会在安静中变得很昂贵
旧标志变成一种code的陈旧状态。它们停留在条件语句、注释、仪表板和运行手册中。然后有人在几个月后编辑“临时” branch,因为没有人移除它。
实际上有效的清理规则很简单:
| 问题 | 做什么 |
|---|---|
| 没有负责人 | 在创建标志时分配一个团队或个人 |
| 没有结束状态 | 决定标志是否被删除、保留或转换为配置 |
| 标志控制太多 | 将其分解为更小、更窄的标志 |
| 核心逻辑隐藏在标志后面 | 将商业规则从渲染条件语句中移除 |
Cleanup 规则: 每个标志都应有一个拥有者、一个目的和一个移除计划。
这也是团队被“信任”问题所伤害的地方。标志名称存在,但回退设置错误。仪表板条目发生了变化,但应用类型没有改变。code 路径已经死亡,但仍然可达。因此,在更大的系统中,类型生成和注册表验证至关重要,即使初始实现看起来很简单。
可观察性会告诉你标志是否有帮助还是只是存在
滚动部署并不是因为标志达到最大暴露度而完成的。它是完成的时团队知道发生了什么。
至少跟踪这些问题:
- 暴露度: 哪些用户看到哪个变体?
- 错误: 标记路径是否触发了更多的客户端故障?
- 采用率: 用户是否使用了你暴露的功能?
- 回滚信号: 您认为什么阈值会让您关闭它?
如果您的旗帜平台无法回答这些问题,您仍然会在发布审查中猜测。
保护您的旗帜并使用CI/CD自动化
坏的部署很明显。坏的旗帜变化更静默,甚至更危险,因为它会在不经过同样的审查路径的情况下改变生产行为。code

对旗帜变化进行生产级别的处理
特征旗帜是发布控制。如果一个团队可以在生产环境中切换旗帜,那么这个团队就可以改变用户看到什么,什么code路径运行,甚至哪些集成触发。那样值得像部署访问一样严格的纪律。
最基本的控制措施很明显:
- 基于角色的访问控制: 限制谁可以更改生产旗帜,并分离读取访问权限和编辑访问权限。
- 审计日志: 保持对标志的更改记录,包括谁修改了它、什么时候修改的以及修改的环境。
- 环境隔离: 测试环境、预览环境和生产环境的标志应该是不同的,以防止测试变更进入生产环境。
- 服务器端检查敏感决策: 客户端标志可以隐藏UI,但不应决定计费权限、权利或授权。
一个常见的错误是把标志控制台当作一个共享的电子表格。产品团队为客户开启某个功能,支持团队关闭它以解决客户的反馈,工程团队则假设没有人修改它,因为没有发布。这种设置在你需要解释一个事故时会出现问题。
打包应用程序会增加风险。在一个Web应用程序中,一个code修复可以快速发布。在一个Capacitor或桌面应用程序中,可能已经在设备上等待远程标志暴露的code版本可能已经存在。构建code的React移动应用程序的团队 React mobile apps with Capacitor 将标志操作放入管道中
标志变得难以信任时,它们不再是你的交付过程的一部分。更安全的模式是将它们管理为同一工作流程的一部分,这个工作流程负责发布功能。
这通常意味着:
__CAPGO_KEEP_0__:React移动应用程序
- 在同一个 PR 中创建或更新标志 code
- 在 CI 阶段验证类型化的标志定义与远程注册表
- 根据目的在环境中设置默认值
- 如果必需的标志缺失或配置错误,则阻止发布
- 为标志设置过期日期或滚动结束状态的任务
我认为有一个简单的规则:如果生产事故可能由标志引起,则 CI 应该在发布之前捕获设置。包括缺失的默认值、重命名的键、陈旧的环境映射和标志在 code 中但不在控制平面中的情况。
如果您需要一个管道结构的起点,请参阅 Git Action CI/CD 工作流 让机密和 __CAPGO_KEEP_0__ 的选择变得乏味
前端团队有时会对标志安全性进行过度复杂化,并忽略了明显的部分。公共客户端 SDK 密钥通常是安全的,如果供应商为浏览器使用而设计。管理员令牌、写入凭证和环境管理密钥不属于此类。它们应该只在 CI 或后端服务中使用。
Frontend teams sometimes overcomplicate flag security and miss the obvious part. Public client-side SDK keys are usually fine if the vendor designed them for browser use. Admin tokens, write credentials, and environment management keys are not. Those belong in CI or backend services only.
__CAPGO_KEEP_0__
在较慢的发布环境中,这个界限更为重要。 Web 团队可以通过快速部署恢复。 移动和桌面团队通常需要将标志系统作为恢复机制。 如果错误的人可以编辑生产标志,或者如果 CI never 验证标志合同,回滚会变得迅速混乱。
超越 Web Feature Flags for Capacitor 和移动应用
有关 React 特性标志的大多数文章都假设可以立即重新部署的 Web 应用。 这个假设一旦您的 React code 内部 Capacitor, Electron或另一个捆绑的运行时
捆绑应用改变了发布的数学
在混合应用中,您通常会将 JavaScript、CSS、资产和配置文件打包在一起,用户不会立即更新。 一项功能可能已经在设备上存在之前您希望任何人使用它。 这改变了标志的作用完全。
最近关于混合发布策略的讨论指出,现有的 React 标志内容很少涉及Capacitor 或 Electron 应用的发布风险模型。 对于这些团队,主要需求是结合标志、目标通道和回滚保护的发布协调层,而不是简单的开关,尤其是避免商店审查延迟时混合应用发布风险讨论).
这正是正确的。 在捆绑应用中,标志不再是关于条件渲染,而是关于 远程激活已经部署的能力.
在移动或桌面React应用中,标志通常控制发布时间比UI存在更重要。
这也是为什么基于频道的分发很重要。如果您正在构建混合应用并需要应用壳和webcode发布模型一起工作,那么 creating React mobile apps with Capacitor 是一个实际的起点。
标志在配对时最有效
对于移动和桌面团队,标志单独无法解决每个发布问题。它们可以隐藏或启用code路径,但无法替代将已修复的资产或逻辑发送到商店。
这就是为什么更强大的模型是:
- deliver code updates outside full store cycles when your platform allows it,
- 将__CAPGO_KEEP_0__更新发送到商店外部
- 针对这些更新的频道或受众
并使用标志来控制激活、回滚和阶段性曝光
If your team ships Capacitor or Electron apps and needs that release-control layer, Capgo 这是一个可供选择的选项。它可以将签名的Web包分发到目标渠道,支持回滚保护和可观察性,并适合混合应用程序的工作流程,特性标志需要与实时更新一起工作,而不是替换它们。
继续阅读:React特性标志:完整的实现指南
如果您正在使用 React特性标志:完整的实现指南 来规划渠道路由和阶段性发布,连接它与 渠道 为渠道的实现细节 渠道 为渠道的实现细节 渠道 为渠道的实现细节 Beta 测试解决方案 为 Beta 测试解决方案中的产品工作流程, 和 版本目标解决方案 为版本目标解决方案中的产品工作流程.