架构决策记录:记录“为什么”,而不仅仅是“做了什么”

架构决策记录:记录“为什么”,而不仅仅是“做了什么”
Daniel Marsh · Spec-First 工程笔记

半年后,团队里某人会盯着代码库问:"为什么我们选了 Kafka 而不是 SQS?"或者"为什么这是一个独立服务而不是一个模块?"如果答案只存在于当初做决定那个人的脑子里,而那个人已经换组甚至离职了,这个问题就变得无解。架构决策记录(ADR)就是为了防止这种情况。它们是简短的文档,记录的不是构建了什么,而是为什么以这种方式构建。

发布于 2026-02-03 · ✓ 已更新 2026-05-06 · 阅读约 8 分钟 · 作者:Daniel Marsh · 审校:编辑政策

ADR 解决什么问题

每个代码库都是数百个决策的产物。大多数决策事后看来是小而显而易见的。但其中有相当一部分是判断性决策:团队权衡了利弊,讨论了替代方案,最终选择了一种并非唯一合理的方案。代码本身记录了选择了哪个方案,但不记录哪些方案被否决了,也不记录否决的原因。

这在可预见的时刻会成为问题。新成员加入团队并质疑某个架构模式。出现性能问题,团队考虑重构,却不知道当前设计正是为了避免另一个更严重的性能问题。依赖升级破坏了某些功能,没人记得当初为什么锁定了旧版本。在每种情况下,缺失的都是同一件事:原始决策背后的推理。

架构决策记录是一份简短的文档,通常一页,在决策做出时捕获该推理。这个概念由 Michael Nygard 提出并形式化,此后被广泛采用。这个想法并不新鲜。对大多数团队而言,新鲜的是真正持续地去做。

ADR 模板

一份有效的 ADR 包含五个部分。每个部分都很简短,整个文档应该能在一屏文本内显示完。如果超出一屏,说明你写得太多了。

# ADR-017: 账单台账使用 PostgreSQL

## 状态
已采纳 (2026-03-14)

## 背景
账单服务需要一个持久化存储来保存交易记录。
我们评估了 PostgreSQL、DynamoDB 和 CockroachDB。台账需要
强一致性、用于对账报告的复杂查询,以及并发发票生成
时的行级锁支持。

## 决策
我们将使用 PostgreSQL 16,基于现有的 RDS 基础设施。
运维团队已经维护 PostgreSQL 集群,查询模式与关系建模
匹配良好。DynamoDB 被否决是因为对账查询需要跨多个
实体类型的连接。CockroachDB 被否决是因为当前流量规模
不足以证明分布式 SQL 数据库的运维复杂性是合理的。

## 后果
- 正面:团队熟悉的技术,强大的生态系统,现有的运维手册
- 正面:对账报告可以使用标准 SQL 连接
- 负面:如果交易量超过每月 1000 万行,纵向扩展有上限
  (如果增长预测发生变化需要重新评估)
- 负面:没有内置的多区域复制,需要第三方工具
  (对当前单区域部署可以接受)

## 相关文档
- 技术规格:/docs/specs/billing-ledger-v2.md
- 容量分析:/docs/analysis/billing-volume-projections.md

状态字段追踪生命周期:提议中、已采纳、已废弃、或已被替代(附替代文档链接)。背景描述影响决策的各种因素,而非解决方案本身。决策说明选择了什么,列出被否决的替代方案及原因。后果同时列出正面和负面影响,因为每个架构决策都涉及权衡。可选的相关文档部分链接到规格、工单或分析文档。

ADR 如何与技术规格互补

技术规格定义要构建什么以及如何构建。它涵盖需求、验收标准、系统设计、API 契约和上线计划。ADR 捕获的是为什么选择某种特定方案而非其他替代方案。这是不同的文档,面向不同的受众,具有不同的生命周期。

规格主要在实现期间被消费。功能上线后,规格基本成为历史文档。ADR 主要在实现之后被消费,当有人需要理解系统为什么是这个样子时。规格回答"我们在构建什么?"ADR 回答"我们为什么以这种方式构建?"

Spec-First 工作流中,两种文档都在编码前编写。规格经过评审来验证"做什么"和"怎么做"。ADR 经过评审来验证"为什么"。只写规格不写 ADR 的团队,最终会拥有文档良好的实现,但缺少文档化的决策理由。半年后,规格告诉你账单服务使用了 PostgreSQL。ADR 告诉你为什么没有使用 DynamoDB。

哪些决策值得记录

并非每个决策都需要 ADR。当整个前端已经是 React 时,再为"我们使用了 React"写一份 ADR 毫无价值。判断标准是:一位合理的资深工程师第一次看到这个系统时,会不会问"你们为什么这样做?"如果会,写 ADR。如果不会,跳过。

通常值得写 ADR 的决策:

通常不需要 ADR 的决策:

这里的判断依据是预期的未来困惑程度。如果未来的读者不会质疑这个决策,它就不需要记录。

ADR 存放在哪里

有两种可行的选择:放在代码仓库中或放在 Wiki 中。两者都有效,正确的选择取决于团队的工作流程。

仓库内存储(例如 /docs/adrs/)有明显的优势。ADR 与它们描述的代码一起版本化。它们经过与代码变更相同的评审流程。可以通过 grep 和 IDE 搜索发现它们。它们不会与代码库脱节,因为它们就在同一个仓库中。缺点是跨多个仓库的 ADR 需要选择一个仓库作为归属地,或者需要一个专门的文档仓库。

Wiki 存储(Notion、Confluence、GitHub Wiki)对非工程师更容易访问,支持更丰富的格式和链接。影响多个服务的跨领域 ADR 有自然的归属地。缺点是漂移:Wiki 页面不经过代码评审,版本控制不够严格,更新速度更快地变得陈旧,因为更新它们不是正常开发工作流的一部分。

我的建议:默认将 ADR 存储在仓库中。只有真正跨越整个组织且没有单一仓库归属地的决策,才使用 Wiki。对大多数团队而言,仓库内方案能产生更好的长期效果,因为维护 ADR 的人就是维护代码的人,使用的是相同的工具。

如何让团队真正去写

ADR 最难的部分不是格式,而是让工程师去写。每个尝试过 ADR 又放弃的团队都在同一个点失败了:流程太重,所以人们不再做了。

解决方法是让 ADR 足够轻量,使编写一份 ADR 花费的时间少于讨论决策的会议时间。五个实用策略:

  1. 在仓库中放置模板。/docs/adrs/TEMPLATE.md 放一个 ADR 模板。工程师应该能在十分钟内复制并填完。如果花更长时间,说明模板字段太多了。
  2. 在决策会议期间就写。主持讨论的人在会后立即写 ADR,趁推理过程还清晰。不要创建后续任务。文档的后续任务永远不会被完成。
  3. 把它包含在 PR 中。如果一个 Pull Request 引入了重大架构选择,ADR 就是 PR 的一部分。评审者可以在审查实现的同时验证推理过程。这也意味着 ADR 由审查代码的同一批人审查。
  4. 降低门槛。一份三段式 ADR,捕获背景、决策和两个后果,比没有 ADR 要有价值得多。不要让完美成为"已写下"的敌人。
  5. 规格评审中引用 ADR。评审规格时问:"这里有需要 ADR 的架构决策吗?"把它作为规格清单的一部分,而不是一个独立的流程。

目标是达到这样一种状态:写 ADR 就像为架构选择写提交信息一样,简短、例行、当场完成。

常见错误

采用 ADR 的团队在头几个月往往会犯同样的错误。尽早识别它们可以挽救这一实践免于被放弃。

事后才写 ADR。在决策做出三个月后写的 ADR 并不比没有 ADR 好多少。推理过程已经模糊。曾经考虑的替代方案已经被遗忘。文档变成了对现状的合理化解释,而非决策过程的真实记录。在决策做出时就写 ADR,而不是等有人来问为什么。

省略被否决的替代方案。ADR 中最有价值的部分是解释什么没被选择以及为什么。"我们选择了 PostgreSQL"是代码已经告诉你的事实。"我们否决了 DynamoDB,因为对账查询需要 DynamoDB 无法高效支持的多表连接"是否则会丢失的知识。如果你的 ADR 没有列出至少一个被否决的替代方案,它是不完整的。

写得太长。ADR 不是设计文档,也不是技术规格。它是一份决策记录。如果你的 ADR 超过一页,要么你在记录多个决策(拆分它们),要么你包含了属于规格的实现细节。保持聚焦于单个决策及其理由。

从不更新状态。当一个决策被新决策替代时,旧 ADR 应标记为已废弃并附上替代文档链接。状态陈旧的 ADR 会侵蚀整个 ADR 集合的信任度。状态字段中简单写上"已被 ADR-042 替代"就够了。

把 ADR 当做审批流程。ADR 记录决策,它不是治理门禁。要求任何技术工作前都必须通过 ADR 审批的团队会制造官僚瓶颈,扼杀开发速度。ADR 应该作为正常工程工作流的一部分编写和评审,而不是一个独立的审批流程。

ADR 在 Spec-First 工作流中的位置

在 Spec-First 方法中,你在编码前先写规格。ADR 自然地融入这个工作流,因为规格编写过程正是架构决策产生的时候。你在纸上设计系统,评估权衡,选择方案。决策发生在规格编写期间。ADR 捕获这些决策。

实际的整合方式是这样的:当你编写技术规格时,任何你在替代方案之间做出选择的部分都会有一份配套的 ADR。规格说"账单台账将使用 PostgreSQL"。ADR 说为什么。规格通过编号引用 ADR。ADR 链接回规格。两者一起经过评审。

这也意味着 ADR 是在上下文最充分的时刻编写的。你刚刚完成了替代方案的评估。你知道否决每个方案的原因。你能清晰地阐述后果。三个月后,这种清晰度会消退。现在捕获它,趁推理过程还清晰,这就是 ADR 的全部意义。

同时采用规格和 ADR 的团队发现,这种组合几乎可以回答未来工程师对系统的所有问题。规格解释了构建了什么以及如何构建。ADR 解释了为什么。两者共同形成一份完整的记录,能够经受住团队更替、组织变化和机构记忆的自然衰退。

ADR 最有价值的是被拒绝的选项

很多 ADR 只记录“我们决定用 X”。半年后没人知道为什么不用 Y。真正有用的是写清当时的约束、被拒绝方案和触发重新评估的条件。

ADR decision quality:
- Decision: use provider webhooks as source of payment truth
- Rejected: poll provider every 5 minutes
- Why rejected: delays dispute updates and increases rate-limit risk
- Consequence: webhook consumer must be idempotent and replayable
- Revisit when: provider offers guaranteed event export API
- Owner: payments platform team

边界:ADR 不要记录每个小实现。它适合那些以后会被质疑的选择:数据库、协议、边界、供应商、兼容性和运维模型。

ADR 和 spec 的关系要写清

ADR 解释为什么选某条路,spec 解释这条路上系统必须如何表现。一个支付 webhook ADR 可以链接到 webhook consumer spec;后者再写字段、状态、测试和回滚。两者分工清楚,文档才不会重复。

关键词:架构决策记录 · ADR 模板 · 技术文档 · spec-first 开发 · 软件架构决策

专题阅读路径

这篇文章归入 Spec-First 开发 主题。先读 Hub,再结合下面的清单、模板或工具落到具体项目里。

交互式生成规格
填写表单,生成完整的功能规格 Markdown——免费使用,无需注册。
试用规格生成器

编辑说明

本文面向软件交付团队,介绍架构决策记录(ADR)。示例均为工程场景说明,不构成法律、税务或投资建议。