Language:Chinese VersionEnglish Version

每个小型工程团队最终都会面临同样的困境:你需要快速发布,但又不能破坏生产环境。标准的解决方案——更长的QA周期、与现实脱节的预发布环境,或者只是祈祷——从来都不是很有效。功能标志优雅地解决了这个问题,而且你不需要LaunchDarkly或每月2000美元的平台就能很好地使用它们。

编辑评论:功能标志多年来一直是企业级工具,但生态系统已经足够成熟,即使是两人团队也能获得80%的好处,同时零供应商锁定。Michael将分解哪些是真正重要的,以及你可以安全忽略的内容。

功能标志解决的核心问题

部署和发布是两回事,将它们混在一起是大多数发布焦虑的根本原因。当你的代码在合并后立即进入生产环境时,每个拉取请求都承担着产品发布的重量。功能标志将这两个事件解耦。你部署默认情况下是惰性的代码,然后在准备好时启用它——针对特定用户、一定比例的流量,或者一次性向所有人发布。

对于小团队来说,这种区别极其重要。没有功能标志,你要么将变更批量整合到不频繁的发布中(缓慢),要么持续部署并祈祷(风险高)。有了标志,你可以持续部署并精确控制曝光度。一个未完成的功能被放在标志后面,对用户不可见,而你可以在多次提交中迭代它。

你真正需要的(以及不需要的)

企业级功能标志平台提供复杂的定位、分析、审计日志和审批工作流。对于一到十名工程师的团队来说,大部分都是不必要的。以下是你真正需要的:

必备功能

  • 每个功能一个布尔开关:开或关。这涵盖了70%的使用场景。
  • 用户级定位:为特定的用户ID或电子邮件地址启用标志,以便你可以使用真实数据在生产环境中进行测试。
  • 百分比发布:向5%的用户发布,监控你的错误率,然后逐步扩展到100%。
  • 即时关闭功能的方法:紧急开关是整个要点。如果出现问题,你只需切换一个标志,而不是部署回滚。

你可以暂时跳过的

  • 复杂的定位规则:“3月后注册的加拿大iOS 18用户”——你几乎肯定暂时不需要这个。
  • A/B测试集成:最终会很有用,但不要让它阻碍你最初的采用。
  • 审批工作流:如果你的团队足够小,每个人都知道要发布什么,那么手动审批只会增加摩擦而不会提高安全性。

三种实用方法,按优先级排序

1. 环境变量(最简单,功能有限)

最快入门的方式是设置环境变量。在您的 .env 文件中设置 FEATURE_NEW_CHECKOUT=true,在运行时检查。这适用于您按部署切换的服务端功能。缺点很明显:更改标志需要重启或重新部署,这违背了大部分目的。

适用于:您有几个不常更改的标志,并且不需要额外的基础设施。

2. 数据库支持的标志(大多数团队的最佳选择)

将标志存储在您现有的数据库中——一个简单的 feature_flags 表,包含名称、启用状态、用户定向和发布百分比等列。构建一个小型管理界面或使用 CLI 工具来切换它们。更改立即生效,无需重新部署。

这是大多数小型团队应该采用的方式。您已经有数据库了。实现可能只需要 200 行代码。您可以获得即时切换、用户定向和百分比发布功能,且无需依赖任何供应商。

最小化的架构如下所示:

CREATE TABLE feature_flags (
  name VARCHAR(100) PRIMARY KEY,
  enabled BOOLEAN DEFAULT false,
  rollout_percentage INT DEFAULT 0,
  allowed_users TEXT,  -- 用户 ID 的 JSON 数组
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);

您的应用程序在每次请求时检查标志。积极使用缓存——标志值的 30 秒 TTL 意味着您可以在半分钟内切换标志并看到效果,而不会频繁访问数据库。

3. 开源平台(当 DIY 无法满足需求时)

当您的标志数量超过 20-30 个,或者您需要审计日志和多环境支持时,可以考虑 Unleash(自托管、开源)或 Flagsmith(开源,提供托管选项)。两者都为每种主要语言提供 SDK、管理界面以及您最初跳过的定向功能。

Unleash 特别值得关注。它可以在单个 Docker 容器中运行,使用 PostgreSQL 进行存储,免费版本可以满足 20 名工程师以下的团队的所有需求。SDK 的开销可以忽略不计——本地缓存每隔几秒与服务器同步一次。

实践中有效的模式

逐步发布

将功能通过标志发布。首先为您的团队启用。然后是 1% 的用户。观察 24 小时的错误率、延迟和支持工单。逐步提升到 10%,然后是 50%,最后是 100%。在每个阶段,您都有一键回滚的路径。这比同时向所有人发布要安全得多,也让发布过程不再那么紧张。

运维标志

并非每个标志都是功能特性。有些是操作控制:一个标志用于在高峰负载期间禁用特别昂贵的数据库查询,一个标志用于从主要支付处理器切换到备用处理器,一个标志用于在调试生产问题时启用详细日志。这些操作标志比功能标志更有价值,因为它们让您能够在不更改代码的情况下获得对系统行为的运行时控制。

主干开发模式的推动者

功能标志使主干开发变得实用。您不再需要与主分支严重偏离的长生命周期功能分支,而是直接将带有新代码的提交放入主分支,代码始终处于集成状态,始终由 CI 测试,但在标志开启前对用户不可见。这消除了合并冲突,降低了 CI 复杂性,并确保每次提交后主分支都可部署。

清理问题(及其解决方法)

这是没人愿意谈论的部分:标志债务。您添加的每个功能标志都是代码中的一个条件分支,使系统更难以理解。拥有 50 个活动标志的代码库有 2^50 种可能的配置,而您几乎测试了其中 none。

纪律很简单,但需要真正的承诺:当一个标志已达到 100% 使用率两周且没有问题时,就将其移除。从数据库中删除标志,从代码中移除条件分支,并删除旧代码路径。安排一个定期任务——每两周审查您的标志列表并清理任何已完全发布的标志。

一些团队通过为标志设置 TTL(生存时间)来强制执行这一点。创建标志时,您需要设置一个过期日期。在该日期之后,标志会出现在”过期标志”仪表板上,不断提醒您直到您清理它。即使您的仪表板只是一个 SQL 查询,这也是一个好做法。

应避免的常见错误

嵌套标志。如果功能 B 只有在功能 A 启用时才有意义,您就创建了一个难以管理的依赖关系图。尽可能保持标志的独立性。

使用标志进行配置。标志是临时切换。如果某些东西永远不会被移除——比如您的速率限制器阈值配置——请使用适当的配置管理,而不是功能标志。

跳过紧急停止开关测试。在将带标志的功能发布给用户之前,测试关闭标志是否真的有效。这听起来很明显,但在事件期间发现其紧急停止开关失效的团队数量高得令人不安。

过度使用标志。并非每个更改都需要标志。错误修复、文本更改和轻微的 UI 调整可以直接发布。保留标志用于具有重大风险的更改或您希望逐步发布的更改。一个好的经验法则:如果您不希望凌晨 2 点回滚此更改,那么它就不需要标志。

我的入门建议

如果你是独立开发者或五人以下的小团队:从数据库支持的标志开始。花一个下午构建一个最小化的实现。在你的下一个功能发布中使用它。体验部署代码直到你允许它才生效的平静,以及在几秒钟内而不是几分钟内关闭故障功能的解脱。

如果你已经在手动使用环境变量来实现这一点:升级到 Unleash。设置只需一个 docker-compose up 命令,工作流程的改进是立竿见影的。

核心观点是,功能标志不是一种工具——它是一种实践。工具可以简单到只是一个数据库表,也可以复杂到是一个管理平台。重要的是将部署与发布分离的习惯,以及来自于你知道随时可以关闭任何东西的操作信心。

关键要点

  • 功能标志将部署与发布解耦——你可以持续交付代码,并单独控制用户曝光,消除大爆炸式发布的压力。
  • 数据库支持的标志系统(200行代码,零供应商成本)可以满足小团队90%的需求:布尔开关、用户定向和百分比发布。
  • 操作标志(紧急停止开关、断路器、调试开关)通常比功能标志更有价值——它们让你可以在不重新部署的情况下对生产环境进行运行时控制。
  • 标志债务是真实存在的。设定一个两周清理规则:如果一个标志已经100%启用两周,就将其从代码库中完全移除。
  • 从简单开始。当你自研方案无法满足需求时,你总是可以升级到 Unleash 或 Flagsmith——但大多数小团队永远不需要这样做。

By Michael Sun

Founder and Editor-in-Chief of NovVista. Software engineer with hands-on experience in cloud infrastructure, full-stack development, and DevOps. Writes about AI tools, developer workflows, server architecture, and the practical side of technology. Based in China.

Leave a Reply

Your email address will not be published. Required fields are marked *

You missed