核心问题

错误多久之后会被发现?

软件工程里的反馈回路,关心的是:

从做出改变,到知道这个改变是否正确,中间隔了多久。

反馈越慢,错误越贵。

反馈越快,系统越容易演化。

为什么反馈回路重要

一个 bug 在不同阶段被发现,成本完全不同。

写代码时发现
  -> 改几秒

本地测试发现
  -> 改几分钟

CI 发现
  -> 改几十分钟

Code Review 发现
  -> 需要上下文切换

测试环境发现
  -> 需要排查部署和数据问题

生产环境发现
  -> 用户受影响,可能要回滚、补数据、写事故复盘

同一个错误,发现得越晚,牵扯越多。

所以成熟工程体系的目标不是“永远不犯错”,而是:

让错误尽早暴露,并让修复路径尽量短。

反馈回路的层级

1. 编辑器反馈

最快的反馈来自写代码时:

  • TypeScript 类型检查
  • lint
  • formatter
  • IDE 提示
  • 静态分析

这些工具的价值是把错误拦在进入代码库之前。

例如:

canAccessCourse(accountId, courseId)

如果 courseId 传成了 organizationId,类型系统最好能立刻提醒。

这就是为什么领域 ID 不应该全是裸 string

可以考虑更明确的类型:

type AccountId = Brand<string, "AccountId">
type CourseId = Brand<string, "CourseId">

这类设计让错误在编辑器阶段暴露,而不是到生产环境才发现。

2. 本地测试反馈

本地测试负责快速验证规则和关键协作。

理想状态:

npm test

能在短时间内告诉你:

  • 状态机有没有坏
  • 权限规则有没有坏
  • 访问权判断有没有坏
  • 核心服务协作有没有坏

本地测试太慢,开发者就会少跑。

少跑之后,反馈回路就被推迟到 CI 或生产。

3. CI 反馈

CI 是团队共同边界。

它回答:

这次改动是否仍然满足项目的最低质量承诺?

CI 通常包括:

  • typecheck
  • lint
  • unit tests
  • integration tests
  • build
  • migration check
  • contract tests
  • 少量 smoke tests

CI 不是仪式。CI 是自动化的认识论系统。

它让团队不必靠“我觉得没问题”合并代码。

4. Review 反馈

Code Review 不应该重复机器能做的事。

机器应该检查:

  • 格式
  • 类型
  • 基础测试
  • 构建
  • 明显规则

人应该关注:

  • 领域概念是否清楚
  • 边界是否放对
  • 是否引入错误抽象
  • 是否有遗漏的失败路径
  • 是否符合系统演化方向

如果 review 里大量时间都在讨论格式、命名风格和低级错误,说明自动反馈不够强。

5. Staging 反馈

测试环境适合验证:

  • 配置是否正确
  • 数据迁移是否可执行
  • 第三方集成是否通
  • 部署产物是否可运行
  • 关键用户路径是否成立

但 staging 永远不能完全代表生产。

它的数据、流量、并发、用户行为都不一样。

所以 staging 只能提供一部分信心,不能替代生产可观察性和渐进发布。

6. Production 反馈

生产反馈来自:

  • logs
  • metrics
  • traces
  • error reporting
  • user reports
  • business dashboards
  • support tickets

生产反馈最真实,也最昂贵。

好的系统会让生产反馈尽可能可控:

  • 小流量发布
  • feature flag
  • 快速回滚
  • 告警清楚
  • dashboard 明确
  • 数据修复路径存在

CI/CD 的真正意义

CI/CD 经常被理解成:

自动构建、自动部署。

但更深层的意义是:

缩短从改变到证据的距离。

CI 让你更快知道:

这次改动是否破坏了已有承诺?

CD 让你更快知道:

这次改动在真实环境里是否成立?

如果部署很痛苦,团队会倾向于攒大版本。

大版本的问题是:

  • 变更多
  • 风险大
  • 回滚难
  • 排查复杂
  • 反馈慢

小批量、频繁、可回滚的发布,反而更安全。

反馈回路和批量大小

反馈速度和变更批量强相关。

大 PR:

改了 80 个文件
包含重构、功能、样式、迁移

问题是:

  • review 慢
  • 测试失败难定位
  • 回滚困难
  • 风险集中

小 PR:

先改名
再抽规则函数
再迁移调用方
再删除旧路径

反馈更快,也更容易确认每一步是否正确。

原则:

小批量改变能缩短反馈回路。

好反馈的特征

1. 快

反馈越快,越容易被使用。

如果一个测试套件要跑 40 分钟,开发者不会频繁依赖它。

2. 准

失败时要能告诉你哪里坏了。

坏反馈:

E2E failed

好反馈:

refund should revoke purchase-based course access
expected canAccessCourse=false, received true

3. 相关

反馈应该和系统承诺有关。

如果告警天天响但没人行动,它就不是反馈,是噪音。

4. 可行动

反馈应该指向下一步。

例如:

payment_success_count normal, course_access_grant_created_count dropped 40%

这比:

error rate high

更容易行动。

慢反馈的坏味道

  1. 开发者不敢重构。
  2. PR 越积越大。
  3. 测试只在合并前跑一次。
  4. 部署需要专门挑时间。
  5. 出问题只能靠用户反馈。
  6. 回滚比修 bug 还痛苦。
  7. 团队靠资深工程师记忆维持质量。

这些都说明系统的认识论基础太弱:

团队不知道怎么快速知道自己是否正确。

反馈回路设计清单

对一个关键业务承诺,比如:

支付成功后用户应该获得课程访问权。

可以设计多层反馈:

层级反馈
类型paymentId、purchaseId、courseId 不混用
单元测试购买访问权规则正确
集成测试payment succeeded 后创建 purchase 和 grant
E2E用户购买后能进入课程页
日志course_access_grant_created
指标grant created / payment succeeded ratio
告警支付成功正常但 grant 创建下降
客服后台能查询用户访问权历史

这就是把一个业务承诺变成一整套证据链。

小结

  1. 反馈回路回答“错误多久之后会被发现”。
  2. 反馈越慢,错误越贵。
  3. 编辑器、本地测试、CI、Review、Staging、Production 是不同层级的反馈。
  4. CI/CD 的本质是缩短从改变到证据的距离。
  5. 小批量改变能缩短反馈回路。
  6. 好反馈应该快、准、相关、可行动。
  7. 对关键业务承诺,要设计多层证据链。