跳过主要内容
移动 更新 教程

如何实施功能标志:2026 年开发工作流

了解如何在开发工作流中实现特性标志。获取 2026 年关于架构、目标、发布和 CI/CD 的指南,适用于 JavaScript、Capacitor 和 Electron 应用程序。

内容营销人员

A risky release usually looks the same. The code passed review, the build succeeded, and the team merged with confidence. Then production traffic hits the new path all at once, support starts seeing errors, and your only rollback option is another deploy under pressure.

That release pattern breaks down even faster in hybrid apps. Your backend can move quickly, but your Capacitor or Electron client may still depend on shipped JavaScript, UI logic, and bundled assets that users already have on device. If you want safer delivery, you need a runtime control layer between “code exists” and “users see it.”

通常,风险发布看起来都一样。code 已经通过审查,构建成功,团队自信地合并了代码。然后,生产流量突然涌入新路径,支持开始看到错误,唯一的回滚选项就是在压力下再次部署。 应用分段发布与完整发布的区别这就是特性标志发挥作用的地方。它们让您可以在发布时将 __CAPGO_KEEP_0__ 保持暗黑状态,仅向特定人群暴露它,并在现实与本地测试不符时快速关闭它。如果您正在处理

阶段发布与完整发布在应用交付中的区别

介绍 从风险发布到控制发布

如何实施特性标志的问题很少会被主动提问。相反,它会在发布后出现疼痛。

一个检查出重写对所有人都有效。一个设置屏幕在 Web 上工作,但在桌面构建上会崩溃。一个移动 shell 正常加载,但客户端 code 后面有一个新标签页,边缘案例没有在 staging 中看到。问题不仅仅是坏 code。问题是发布和部署被视为同一事件。

特性标志通过将这两个时刻分开来解决这个问题。团队首先将 code 发送,然后通过条件逻辑在运行时评估标志。Datadog 在其中描述了核心模式的清晰表述 功能标志实现概述:应用程序在运行时检查配置并将用户路由到新路径或旧回退路径。因此,标志对于逐渐部署、分组目标和立即禁用而无需重新部署整个应用程序非常有用。

实践规则: 如果禁用一个风险功能仍然需要重新部署,那么您还没有构建一个真正的功能标志系统。

这在混合堆栈中尤其重要。您的服务器可能会决定谁应该看到一个功能,但您的客户端仍然需要在Web、Capacitor和Electron中一致地行为。这意味着标志系统不能成为随机组件中的后想象。它必须成为您的发布设计的一部分。

成功的团队将标志视为运营工具。他们使用它们来控制不完整的工作、首先向内部用户发布,并在生产中出现意外时快速恢复。

选择功能标志架构

在将标志传播到代码库之前选择架构。如果您晚些时候进行此工作,则会在调试服务器、Web应用程序、Capacitor shell和Electron构建之间出现分歧,而不是调试功能本身。

关键决策很简单。标志真理在哪里,谁评估它?

发布控制始于真理来源

功能标志系统只有在应用程序可以问一个可信的来源并应用当前决策时才有用。在实践中,混合团队通常需要两个层次一起工作:

  1. 控制平面 定义旗帜状态、目标规则、审计历史和杀switch
  2. 物流路径 将正确的code和配置快速传递给正确的客户端

第二部分在通用旗帜教程中被忽略。 服务器端旗帜可以隐藏一个功能,但它无法将修复的客户端包传递给一个损坏的Capacitor或Electron应用。 对于混合发布,旗帜和实时更新需要一起工作。 旗帜控制暴露。 更新系统将准确的客户端code传递给旗帜后面

对于已经通过该设置的React和混合团队,这个 指南展示了React混合应用的架构选择如何影响组件边界、状态流和滚动安全 通常,选择其中一个模型

在内部构建

  1. 购买SaaS平台
  2. 自己运行开源系统
  3. __CAPGO_KEEP_0__

正确的选择取决于运营约束,而不是个人喜好。请直接提问。您是否需要在API响应中进行服务器端评估?您是否需要在移动设备上设置离线默认值?产品和支持是否需要仪表板?您是否需要为受管控的更改配置审计日志?您的团队是否能够操作每个客户的SDK、缓存失效和目标逻辑?

构建、购买或自主托管

以下是我与团队一起规划在Web、Capacitor和Electron上发布的决策表。

因素 构建(内部) 购买(SaaS) 开源(自主托管)
控制 schema、评估规则和数据存储的完全控制权 在产品成熟度方面更快的产品成熟度,拥有更少的基础设施控制权 在现有的平台模型中拥有高控制权
初始设置 快速用于基本的布尔值,但在添加目标和治理后会变慢 通常是最快的路径 中等的设置和集成工作
运营负担 您的团队负责可用性、SDK 行为、审计和过期标志清理 供应商拥有大部分平台 您的团队负责托管、升级和可靠性
目标复杂性 经常低估了第一次内部发布请求后 通常可以直接使用 可用,但您仍需要操作和调优它
混合应用适用 如果您还能构建出好的客户端交付路径,那么就可以精确匹配您的堆栈 取决于SDK的质量和离线行为 如果您可以将平台适应到您的客户端,那么这是一个不错的选择
长期维护 一旦标志成为发布操作的一部分,成本最高 订阅成本取代了平台所有权 降低的构建成本和持续的运营成本

这是一个让团队措手不及的权衡。构建一个标志服务并不是难事。构建一个能够处理目标、本地缓存、环境推广、审计日志、标志过期和在服务器和客户端上一致评估的标志服务,这是真正的平台工作。

我见过团队在一个冲刺中构建了一个可用的内部系统。六个月后,他们正在维护管理员屏幕、QA覆盖逻辑、每个环境的漂移检查和自定义code以在应用启动后安全刷新客户端配置。第一版解决了布尔值。第二版变成了发布基础设施。

开源和SaaS平台可以减轻负担,但它们并不能消除您的混合特定问题。您仍然需要决定评估发生在哪里、客户端可以缓存结果多久、应用在离线状态下做什么以及如何恢复当客户端包已经在设备上时。Unleash清晰地列出了所有移动部分在其 功能标志系统概述:成熟的设置包括管理服务、存储、API、SDK和更新机制

如果您的回滚计划是“关闭标志”,请验证客户端是否已经安全地 fallback code。如果没有,请将标志 pair 与 live updates 配对,以便禁用暴露并在不等待商店发布的情况下将修复推送。

这是混合角度改变架构决策的地方。 服务器端标志回答“谁应该看到这个?” Live update 系统,如 Capgo 答案“用户应该运行哪个 code?” 使用两者。 将特性推送给内部用户,仅将更新的客户端包推送到该群体,然后根据遥测数据保持清洁的扩大暴露。 这种模式比标志本身提供了更紧密的爆炸半径控制。

如果您在内部构建,保持范围狭窄并明确。 定义标志模式,集中评估规则,添加管理 API,记录每次更改,并在第一个标志发布之前设置移除策略。 如果您购买,请在糟糕的网络条件下测试 SDK 行为,并在应用程序重启时测试。 如果您自行托管,请预算工程时间用于升级、on-call 负责人和客户端集成工作从第一天开始。

跨平台应用程序的核心实现模式

混合应用程序通常在边界上失败,而不是在标志定义本身上失败。

常见的故障模式是众所周知的。 Web code 在启动时读取一个标志值,一个 Capacitor 插件稍后检查一个缓存副本,一个 Electron 窗口在稍微不同的用户上下文中评估相同的标志。 现在,发布在各个平台上是不一致的,回滚变得像猜测一样。

一位戴眼镜的男子坐在桌子旁,凝视着复杂的code在大型电脑显示器上。

从简单开始,快速集中

每个功能标志都从 if/else:

if (flags.newCheckout) {
  renderNewCheckout();
} else {
  renderLegacyCheckout();
}

这对于第一次提交是可以的。它停止被认为是好的,一旦同一标志被五次提交,并且每层都有不同的解释。

马丁·福勒的 功能开关模式文章 仍然给出了正确的基线。保持评估逻辑集中,保持条件语句靠近流程的边缘,而不是将它们散布在低级组件中。

在跨平台应用中,通常有用的评估点是:

  • 服务器请求设置 用于SSR、API形状或初始配置传递
  • 客户端引导 在您加载身份、设备和环境上下文之后
  • 路由或屏幕边界 根据标志状态,整个流程会有所不同

避免在嵌套组件、原生桥接和辅助工具中深入评估相同的标志。这一模式会迅速导致漂移。

传递决策,而不是原始标志

成熟的实现将供应商标志值与应用程序决策分开。

您的标志提供者会回答以下低级别的问题: newCheckout=true您的应用程序应该消费更高级别的决策,如 showNewCheckout, enableDesktopSidebar,或 allowBackgroundSync。这个层次是您编码商业规则、平台约束和回退行为的地方。

这种额外的间接性会很快报销。

它保持了React组件的清洁。它减少了对一个SDK的耦合。它还为您提供了一个地方来回答混合团队经常遇到的问题:这个用户是否具有标志和正确的客户code?

最后一点对于Capacitor和Electron很重要。 服务器可以立即切换暴露,但客户端仍需要code来安全地渲染功能。 将标志评估与目标捆绑交付配对起来,这是您关闭这一差距的方法。 Capgo的指南 实时更新与用户分组 展示了运营模型。评估哪些用户应该获得该功能,然后将匹配的客户端更新发送给该群组,而无需等待应用商店审查。

一个实用的TypeScript模式

以下是一个模式,相比于直接在组件中进行检查,能够更好地扩展。

type UserContext = {
  userId?: string;
  country?: string;
  plan?: 'free' | 'pro' | 'enterprise';
  platform: 'web' | 'capacitor' | 'electron';
  isInternal?: boolean;
};

type RawFlags = {
  newCheckout: boolean;
  desktopSidebarRedesign: boolean;
  smartSync: boolean;
};

class FeatureFlagService {
  constructor(private flags: RawFlags, private user: UserContext) {}

  get decisions() {
    return {
      showNewCheckout: this.flags.newCheckout && this.user.plan !== 'free',
      showDesktopSidebar: this.user.platform === 'electron' && this.flags.desktopSidebarRedesign,
      enableSmartSync: this.flags.smartSync && this.user.country !== undefined,
    };
  }
}

在应用程序的顶部评估一次:

async function bootstrapApp() {
  const user = await getUserContext();
  const flags = await fetchFlagsForUser(user);

  const featureService = new FeatureFlagService(flags, user);
  const decisions = featureService.decisions;

  startApp({ user, decisions });
}

然后保持UI简单:

type AppProps = {
  decisions: {
    showNewCheckout: boolean;
    showDesktopSidebar: boolean;
    enableSmartSync: boolean;
  };
};

function App({ decisions }: AppProps) {
  return (
    <>
      {decisions.showDesktopSidebar ? <NewSidebar /> : <LegacySidebar />}
      {decisions.showNewCheckout ? <CheckoutV2 /> : <CheckoutV1 />}
    </>
  );
}

这种结构在不同屏幕上提供了一致性,简化了测试,并且在滚动完成后可以更轻松地移除。

将平台和更新准备添加到决策层

混合应用程序需要一个额外的检查,通用标志教程经常会忽略。功能不仅仅因为远程标志说是的就开启。它应该在安装或实时更新的客户端可以支持它时才开启。

这意味着您的决策层通常需要的输入超过原始标志:

  • 当前应用程序版本
  • 当前实时捆绑包版本
  • 平台
  • 离线状态
  • 原生能力可用性

决策对象可以直接表达:

type RuntimeContext = {
  appVersion: string;
  bundleVersion?: string;
  isOffline: boolean;
  hasNativeBiometrics: boolean;
};

function buildDecisions(flags: RawFlags, user: UserContext, runtime: RuntimeContext) {
  return {
    showNewCheckout:
      flags.newCheckout &&
      user.plan !== 'free' &&
      runtime.bundleVersion === 'checkout-v2',

    enableSmartSync:
      flags.smartSync &&
      !runtime.isOffline,

    enableBiometricUnlock:
      flags.smartSync &&
      runtime.hasNativeBiometrics &&
      user.platform === 'capacitor',
  };
}

这是一个实际的权衡。决策层变得更加复杂,但应用程序变得更安全。跳过这一步的团队通常在回滚时发现差距,当标志关闭但不兼容的code已经在设备上安装时,或者标志对从未接收到所需捆绑包的用户打开时。

使用确定性分桶进行任何滚动逻辑

百分比滚动逻辑也应放在一个地方。不要在每次渲染或应用启动时为用户随机 assignments。使用稳定的标识符和确定性哈希函数,以便相同的用户始终落在相同的分桶中。

function isInRollout(featureName: string, userId: string, rolloutGate: number): boolean {
  const bucket = stableHash(`${featureName}:${userId}`) % 100;
  return bucket < rolloutGate;
}

精确的哈希函数不如行为重要。相同的输入始终应落在相同的分桶中。如果您还提供实时更新,请将分桶输入与用于分发捆绑包的受众规则保持一致。否则,您可以向从未接收过支持code的用户暴露特性标志。

最后一个规则有助于避免大量后期清理。将标志检查从可重用的叶组件中移除,除非该组件仅用于该实验。将分支放在路由、屏幕或服务边界处,让剩余的树渲染一个选择的路径。

战略滚动和受众目标

A rollout plan gets tested the first time production behaves differently for one slice of users than another. A checkout flow works on desktop Electron, fails on older Android WebView builds, and support needs to know who is exposed right now. That is the point where a boolean flag stops being enough.

五步图解软件开发中的战略特性标志发布和控制特性发布。

新支付流程的发布故事

假设您正在推送 new-checkout 在一个 Capacitor 应用程序中,使用 Electron 桌面构建。 UI 变化位于服务器端标志后面,但支持逻辑的一部分作为客户端 code 发送。如果这两个系统不一致,用户可能会在他们有包之前看到标志,或者在他们应该看到功能之前就有包。

首先使用员工账户和 QA 设备。然后在一个平台上,例如 Electron,只有优选的 beta 用户。移动设备保持旧路径。然后按分组和百分比扩展,同时监控错误率、支付失败和支持票数。直到在每个支持的平台上,滚动部署已经经受了真实流量的考验,才将旧的检出路径关闭。

该特性实践策略如下:

  • 内部分组首先: 开发人员、QA、支持和演示账户
  • beta 用户按平台: 早期访问用户,但只在您信任的应用程序版本和运行时上
  • 生产环境按步骤: 逐步增加曝光度并在出现回归时暂停
  • 回退策略仍然保持在线: 直到新路径在生产环境中稳定之前,旧路径仍然可调用

对于混合应用,发布策略还需要一个交付策略 Live update 用户分段策略适用于 Capacitor 应用 展示如何将匹配的客户端包发送到旗帜系统目标的同一群体中。这个连接很重要,因为如果旗帜和发送的 code 的受众规则不同,发布控制就很弱

在生产环境中有效的目标规则

好的目标规则使用的属性可以在事件发生时解释和复现。常见的属性包括平台、应用版本、地区、账户等级、内部用户状态和beta注册

坏的目标规则依赖于出现较晚或频繁变化的值。会话本地状态、部分同步的用户字段或客户端属性会导致难以调试的不匹配

使用团队可以阅读的规则而不需要打开三个仪表板 internal, beta_mobile, 和 enterprise_desktop_v2 比匿名分段ID更容易操作。支持团队应该能够快速回答一个问题:为什么这个用户获得了这个功能?

让我们明确一下一个更重要的权衡。 服务器拥有目标可以将策略集中化,但混合应用程序仍然需要足够的客户端上下文来应用安全的本地回退,当网络速度慢或不可用时。 通常的模式是让服务器决定暴露并让客户端执行兼容性检查,例如运行时、捆绑包版本或本机能力。

杀switch是设计的一部分

杀switch是从第一天开始的发布设计的一部分。它不是后期清理工作。

对于客户端功能,保持之前的路径存活直到新路径通过您的主要族群的真实生产流量。 如果某个区域或某个运行时的支付失败率突然升高,您应该能够立即禁用该功能而不必等待应用商店的审查。

混合应用程序添加了另一个层次。 服务器端标志可以隐藏一个破碎的路径,但它无法修复code已经在设备上。 Live update系统,如Capgo,可以填补这一差距。 您可以关闭该功能,然后将修正的捆绑包推送到受影响的族群,而不是等待下一个完整的发布周期。

这组合使发布成为操作而不是理论。 标志控制暴露。 目标限制爆炸半径。 实时更新快速修复客户端,当运行时行为和已发货code分离时。

测试可观察性和标志卫生

A feature flag adds code paths, timing issues, and state you now have to reason about in production. If you do not test and observe that state directly, the flag shifts risk around instead of reducing it.

故意测试两个 branch

将每个标志视为同一代码库中的两个发布。旧路径仍需要保护,而新路径则需要在真实应用条件下证明其正确性。

在单元测试级别,注入标志决策,以使测试保持确定性。在集成和端到端测试级别,为QA和CI提供一个受控的覆盖。不要依赖实时目标规则来运行测试。这些规则会改变,缓存会过期,突然一个不稳定的测试会告诉你更多关于发布时间而不是产品行为。

对于混合应用,测试标志状态可以从应用状态漂移的时刻:

  • 启用和禁用路径: 保持两者覆盖率,直到标志被移除。
  • 边界小组: 验证员工、beta、付费、区域和匿名用户规则分别。
  • 发布、恢复和刷新流程: 许多Capacitor和Electron应用程序在这些点重新评估状态。
  • 离线fallback行为: 确认客户端在网络不可用时使用最后一次已知的良好决策或安全默认值。
  • 兼容性: 如果标志暴露了通过live update传递的code,则验证应用程序不启用当前无法支持的UI。

最后一点容易忽略。 服务器可以决定用户应该看到的功能,但客户端仍然必须确认安装的包和本机运行时可以安全地执行它。

观察标志,而不是仅仅观察功能。

仪表板应该让您快速回答三个问题:谁看到标志?哪个code路径运行?哪个包版本在运行时处于活跃状态?

团队经常将标志连接起来并停止。 然后在生产中出现错误峰值, nobody 能够确定问题是否来自标志的code,还是来自一个受众分组,还是来自一个过时的客户端包。 解决方案很简单。 将评估的标志状态添加到分析事件、日志、跟踪和错误报告中。 不要仅仅 feature=new_checkout。 将实际决策、产生它的规则或团体以及执行它的客户端版本记录到日志中。

通常一个简单的事件形状就足够了:

{
  "event": "checkout_started",
  "flag_new_checkout": true,
  "flag_rule": "beta_users_us",
  "app_version": "5.4.1",
  "bundle_version": "2026.06.13-2",
  "platform": "capacitor-ios"
}

这种结构使生产调试更快。 您可以将坏的发布规则与坏的包分开,并且可以看到哪个平台正在失败,而另一个平台是健康的。

对于混合应用程序 实时更新指标Capacitor应用程序 帮助关闭发布控制和运行时证据之间的差距。当您将特性暴露数据与捆绑采用数据结合起来时,可以确定回归是否来自标志决策、已发 JavaScript 还是这两者的交互。

A标志没有可观察性就像带有仪表板复选框的隐藏复杂性。

清理是实现的一部分。

标志债务迅速转化为code债务。

最糟糕的标志是成功但没有被删除的标志。它们让死分支保持活跃,混淆入职工程师,并在发布决策结束后扩大测试矩阵。在混合应用中,它们还会使live update工作更辛苦,因为您需要为不再重要的状态保留兼容性逻辑。

创建标志时,设置以下卫生规则:

  1. 分配负责人。
  2. 记录删除条件。
  3. 立即打开清理任务。
  4. 删除完成回滚后死code。
  5. 存档或删除标志条目,以便支持和工程团队不将其视为仍然活跃。

我还建议为通过服务器端标志和实时更新发布的团队制定一个实用的规则。如果标志仅用于保护旧版和新版客户端捆绑之间的短期迁移,请为其设置一个短期过期日期,并与发布负责人进行审查,而不是将其视为一般的后台清理。这些临时标志在Capacitor和Electron应用中迅速增加,尤其是在您在等待完整商店发布时修补生产行为时。

CI/CD和Live Update自动化和超级旗帜

手动旗帜工作流程不太适合。它们也会在最糟糕的时刻失败,通常是在热修复期间。

成熟的设置将旗帜与构建、测试和部署应用程序的相同交付过程绑定。

截图来自https://capgo.app

将旗帜创建作为交付的一部分

当特性分支合并时,管道应该已经知道足够的信息来创建或验证将保护它的旗帜。那样做并不是意味着每次提交都需要一个新的开关。它意味着释放控制应该是系统化的,而不是由最后一次合并的人掌握的部落知识。

有用的自动化通常包括:

  • 旗帜模式检查: 在合并之前验证名称、所有者和过期计划。
  • 环境默认值: 新风险特性应该在生产环境中禁用,除非explicitly批准。
  • 发布说明中包含旗帜状态: 支持和QA需要知道哪些功能在构建中被锁定。
  • 清理提醒: 旧标志应该在工程流程中出现之前成为永久垃圾。

如果您将其集成到移动和混合部署管道中, 为Capacitor应用设置CI/CD 是同一问题的运营侧。

Live Update改变了方程式。

混合应用需要与纯Web应用不同的策略。

一个服务器端标志决定谁应该看到一个功能。但有时code后面的功能需要在应用二进制已经在用户手中时改变。在Capacitor和Electron中,这会创建一个发布间隙。标志可以隐藏或暴露一个路径,但它不能自己重写客户端包。

这就是为什么live update系统与功能标志配对得如此好。标志控制 谁 应该看到功能。更新频道控制 哪个客户端code 那些用户收到的。例如,团队可能会使用LaunchDarkly或Unleash进行实时目标定位,并使用 Capgo 将更新的 JavaScript、CSS、复制、配置和资产直接传递给特定渠道中的 Capacitor 或 Electron 应用程序,而无需等待应用商店的审核。

将更新的JavaScript、CSS、复制、配置和资产传递到Capgo或Electron应用程序的特定频道,而无需等待商店审查。

  • 这种结合在混合环境中的目标发布特别有效: 服务器端目标:
  • 在运行时选择受众。 客户端传递:
  • 推送支持该功能的特定捆绑包。 运营恢复:
  • 禁用功能、发送修复捆绑包或两者都可以。 保持 Web、桌面和移动发布逻辑一致,即使交付机制不同。

本指南提供了团队在实践中如何处理工作流程的具体视图。

如果您严肃地考虑在混合堆栈中实现特性标志,应在层次上思考。一个层次决定暴露。另一个层次传递 code。第三层观察发生了什么。当这些层次是分开但协调的时,发布不再感觉像不可逆的赌注,而开始像受控的操作一样。


Capgo 适用于在 CapacitorJS 和 Electron 应用中运输的团队。它提供实时更新、基于频道的目标、回滚控制、可观察性和 CI/CD 集成的 Web 包传递,这使得它成为在您的发布策略依赖于两者:运行时控制和快速客户端修复时,实用补充服务器端特性标志系统的实用补充。

实时更新Capacitor应用

When a web-layer bug is live, ship the fix through Capgo instead of waiting days for app store approval. Users get the update in the background while native changes stay in the normal review path.

最新博客

Capgo 为您提供创建真正专业的移动应用所需的最佳见解。