技术决策与 ADR

技术决策与 ADR——怎样记录上下文、约束、备选方案和后果,把团队的关键决定从口头记忆变成可追溯、可复盘、可修订的工程资产。

技术决策与 ADR

决策真正稀缺的,不是结论,而是上下文

很多团队其实并不缺结论。

缺的是:

  • 这个结论为什么当时成立
  • 当时有哪些约束
  • 还认真考虑过哪些方案
  • 团队接受了什么代价

时间一长,这些背景就会流失。

后来的人只会看到“系统现在是这样”,却不知道:

  • 这是主动选择
  • 还是历史偶然
  • 是长期方向
  • 还是临时妥协

ADR 的价值,就在于把这些背景留下来。

先把 ADR 讲清楚

ADR 是 Architecture Decision Record

简单说,就是一条关键技术决策的记录。

它不是长篇设计文档的替代品。

它更像是一个轻量、稳定、长期可查的决策档案。

它要回答的核心问题通常是:

  • 做了什么决定
  • 在什么上下文里做的
  • 为什么这样选
  • 带来了什么后果

如果 RFC 更像“待评审提案”,那 ADR 更像“已经做出的关键决定的留档”。

为什么团队会反复争同样的问题

很大一个原因是,过去的决策只有结果,没有解释。

于是新成员加入、系统边界变化、业务压力变化时,团队很自然会问:

  • 为什么不用另一个方案
  • 为什么历史上要这样拆
  • 为什么这里非要维持兼容

如果没有决策记录,这些问题每次都只能靠“老人回忆”。

这会带来两个问题:

  • 讨论成本越来越高
  • 决策质量越来越依赖少数人记忆

ADR 的作用,就是把这类知识从口头经验里解耦出来。

什么样的决定值得写 ADR

不是所有决定都值得写。

如果连一个组件名怎么取都写 ADR,团队很快会失去耐心。

更适合写 ADR 的通常是这些决定:

  • 会影响长期架构走向
  • 会绑定团队一段时间内的约束
  • 回头看时很容易被重新质疑
  • 会影响多个项目或多个团队

例如:

  • 状态管理策略
  • API 规范策略
  • monorepo / 多仓方案
  • SSR / CSR / RSC 的总体边界
  • 日志与监控标准
  • 前端构建链路线

ADR 解决的不是“写文档”问题,而是“保留决策记忆”问题

很多系统会经历:

  • 原始作者离开
  • 业务阶段变化
  • 新技术生态变化
  • 组织结构调整

这时最容易丢失的,不是代码本身。

而是“当初为什么这么做”。

代码能告诉你当前形状。

ADR 才能告诉你决策逻辑。

一条好 ADR 的核心结构

一个经典且实用的 ADR 通常至少包含:

标题

要具体到决策本身。

例如:

  • “采用 TanStack Query 统一服务端状态管理”
  • “前端应用统一迁移到 pnpm workspace”

不要写成:

  • “技术优化”
  • “系统升级”

状态

常见状态包括:

  • proposed
  • accepted
  • deprecated
  • superseded

状态的价值在于告诉后来的人:

这不是一条永远正确的真理,而是一条有生命周期的决定。

上下文

这是最值钱的部分之一。

你需要说明:

  • 当时系统处于什么阶段
  • 主要痛点是什么
  • 有哪些关键约束
  • 哪些现实条件影响了决定

决策

明确写出最终选择。

不要写成模棱两可的态度。

后果

写清楚这个决定带来的收益和代价。

这也是很多文档最容易偷懒的地方。

备选方案

说明为什么没选其他方案。

不是为了证明“我们考虑过”。

而是为了给未来重评估提供基线。

上下文为什么一定要写

因为离开上下文,很多决策都无法被公平评价。

比如今天看一个旧决定,可能会觉得:

  • 为什么不用更现代的工具
  • 为什么不直接一步到位

但如果当时的现实是:

  • 团队对某技术完全不熟
  • 发布窗口很紧
  • 存量系统迁移成本极高
  • 关键依赖不兼容

那同一个决定就会显得合理得多。

这就是为什么 ADR 不是给过去辩护。

而是保留当时判断所依赖的现实。

“后果”这一节为什么非常重要

很多人写 ADR 时,重点都在“为什么这么选”。

但从长期维护角度看,“选了之后意味着什么”同样重要。

后果里应该诚实写出:

  • 会多出哪些维护成本
  • 会失去哪些旧能力
  • 团队需要补哪些新习惯
  • 未来可能触发什么迁移点

如果只写收益,不写代价,ADR 的可信度会下降很多。

ADR 和 RFC、design doc 的关系

可以把它们看成不同层次的文档:

  • RFC 更像正在讨论中的提案
  • design doc 更偏设计与实施细节
  • ADR 更偏最终关键决策留档

它们不是互斥关系。

一个成熟的团队经常是这样衔接:

  • 先在 RFC 里讨论方向
  • 在 design doc 里展开实现细节
  • 用 ADR 把关键结论长期留存

技术决策为什么不能只靠口头共识

因为口头共识会随时间变形。

今天大家都清楚的事,三个月后、半年后、新人加入后,可能就不再清楚。

而且口头共识还有一个问题:

大家经常以为自己理解一致,实际理解并不一致。

书面决策至少能逼迫团队把模糊点说清楚。

一个成熟 ADR 的写法,应该尽量避免什么

1. 避免口号化

比如:

  • “为了提升效率,采用新方案”
  • “为了架构升级,进行统一改造”

这些都太空。

2. 避免把历史改写成必然

现实中的决定几乎都带有妥协。

不要把过去写成“当时只有这一条路”。

3. 避免只写结论,不写被放弃的东西

如果没有备选方案,后来的人很难判断这个决定是否还值得继续。

4. 避免决策状态长期不更新

如果一个决定已经被替代,却没有写明,ADR 就会开始误导人。

决策索引为什么值得做

当 ADR 累积多起来后,团队很容易再遇到一个问题:

文档有了,但很难找。

所以通常值得做一个索引:

  • 编号
  • 标题
  • 状态
  • 日期
  • 被谁替代

这样后来的人就能更快理解系统演进脉络。

ADR 最常见的几个价值

避免重复争论

不是说以后永远不能再讨论。

而是至少先站在同一份历史事实之上讨论。

帮助 onboarding

新人可以更快理解系统为什么长成今天这样。

帮助复盘

当某个决定后来出现问题时,可以更清楚地回看当时 assumptions 是否失效。

降低单点记忆依赖

这点在人员流动后会非常明显。

一个典型例子:为什么只看结果会误判

假设团队现在在用 Webpack,后来的人很容易直接下结论:

  • 为什么不早点换 Vite

但如果 ADR 里写清楚当时的背景:

  • 项目是大型多页面存量系统
  • 自定义 loader 与 plugin 很多
  • 构建链路与发布平台深度耦合
  • 当时关键依赖对新工具链支持并不稳定

那这个决定就不再只是“保守”。

它可能是一种在现实约束下的理性选择。

这也是 ADR 真正有用的地方。

技术决策为什么总要和组织现实一起看

很多决策不是纯技术题。

它们会同时受这些因素影响:

  • 团队熟悉度
  • 招聘市场
  • 上线节奏
  • 合规要求
  • 跨团队协作方式

如果 ADR 只写技术优缺点,不写这些现实约束,后续读者仍然很难真正理解它。

中国互联网语境下,ADR 最容易遇到什么阻力

比较常见的是:

  • 节奏快,觉得写文档太慢
  • 决策经常在会议里快速拍板
  • 业务变化快,担心写了也过时

这些担心都真实存在。

所以国内团队落 ADR,通常更适合轻量化:

  • 文档短一些
  • 聚焦关键决定
  • 和实际项目节点绑定

不要一上来就做成厚重制度。

海外互联网语境里,为什么 ADR 更容易长期保留

很多海外团队异步协作更多,跨时区沟通更常见,也更强调书面决策和演进记录。

这会让 ADR 更容易成为长期协作接口。

这背后并不只是文化偏好。

它本质上是在回答:

当团队无法时时同步时,关键决策如何被准确传递。

这类主题为什么很适合做 senior 表达训练

因为它特别容易被讲成模板题。

比如:

  • ADR 有标题、状态、上下文、决策、后果

这当然没错。

但真正更像 senior 的讲法会继续说明:

  • 为什么上下文比结论更稀缺
  • 为什么后果一定要诚实写
  • ADR 和 RFC、design doc 怎么配合
  • 为什么决策要有生命周期,而不是写完就封存

建议实践

实践 1:补写一条真实 ADR

练什么:

把口头记忆转成可追溯决策。

最小交付物:

一条完整 ADR。

验收标准:

  • 有明确上下文
  • 有清晰决策
  • 有诚实后果

常见误区:

  • 写成结果公告

实践 2:给一组 ADR 建索引

练什么:

把零散决策连成演进脉络。

最小交付物:

一页 ADR index。

验收标准:

  • 能看见状态和替代关系
  • 能快速定位关键决策

常见误区:

  • 只有标题列表,没有状态和时间

实践 3:做一次“决策失效”复盘

练什么:

理解技术决策的生命周期。

最小交付物:

一篇针对旧 ADR 的 revisiting note。

验收标准:

  • 能说明当时为什么成立
  • 能说明现在为什么需要调整

常见误区:

  • 只用今天的眼光否定过去

延展阅读