架构决策记录(ADR):完全指南
每个软件系统都由数百个决策塑造。PostgreSQL还是MongoDB。REST还是GraphQL。单体还是微服务。事件驱动还是请求-响应。每个选择都约束着未来的选择,它们共同定义了架构。
问题在于决策是不可见的。代码展示了_构建了什么_,而不是_为什么这样构建_。选择PostgreSQL六个月后,没人记得团队是否考虑过DynamoDB,性能要求是什么,或者为什么最终一致性不可接受。决策融入了代码,但推理过程丢失了。
架构决策记录(ADR)通过在简短、结构化的文档中捕获每个重要决策来解决这个问题。它们是软件工程中回报最高的文档实践之一——简单易用、维护成本低,在需要时无价。
本指南涵盖从基础到高级实践的所有内容:什么是ADR、如何编写、可以立即采用的模板、常见陷阱,以及如何使用现代工具将ADR集成到工作流中。
什么是架构决策记录?
架构决策记录是一份记录单一架构决策的简短文档。重点在于_单一_和_简短_:
- 单一:一个ADR,一个决策。"订单服务使用PostgreSQL"是一个ADR。"制定整个数据库策略"是设计文档,不是ADR。
- 简短:ADR应该能放在一页纸上。如果更长,你可能在记录多个决策或包含了太多实现细节。
这个概念由Michael Nygard在2011年的一篇博客文章中推广。此后,从初创公司到政府机构都采用了ADR。英国政府数字服务部门、Spotify以及无数工程团队都将ADR作为标准实践。
ADR捕获什么
每个ADR回答四个基本问题:
- 背景是什么? 什么情况或问题促使了这个决策?
- 我们决定了什么? 我们做出了什么具体的架构选择?
- 考虑了哪些替代方案? 还有什么其他选项?
- 后果是什么? 这个决策带来了什么权衡、风险和影响?
就这些。四个问题。一个写得好的ADR可以在不到一页纸内回答所有四个问题。
ADR不是什么
- 不是设计文档。 ADR不描述如何实现某个东西。它们描述_为什么_选择了特定方法。
- 不是RFC。 RFC(征求意见)是供讨论的提案。ADR记录的是_已结束的_决策(尽管ADR可以从提案开始并转变为已接受)。
- 不是会议记录。 ADR捕获结果,而不是导致结果的讨论。
- 不是全面文档。 ADR补充架构图、API规范和运维手册。它们不替代这些。
标准ADR模板
存在多种ADR模板,但大多数是同一核心结构的变体。以下是一个平衡完整性与简洁性的实用模板:
# ADR-NNNN: [描述决策的简短标题]
## 状态
[已提议 | 已接受 | 已弃用 | 被ADR-XXXX取代]
## 日期
[YYYY-MM-DD]
## 背景
[描述情况。我们在解决什么问题?存在什么约束?
有什么影响因素?要具体——在相关处包含数字、截止日期、
团队能力和技术要求。]
## 决策
[清楚简洁地陈述决策。使用主动语态:
"我们将使用X"而不是"应该考虑X"。]
## 考虑的替代方案
### [替代方案1]
- 优点:...
- 缺点:...
- 拒绝原因:...
### [替代方案2]
- 优点:...
- 缺点:...
- 拒绝原因:...
## 后果
### 积极方面
- [什么变得更容易或更好]
### 消极方面
- [什么变得更难或更糟]
### 风险
- [什么可能出错]
## 相关决策
- [相关ADR的链接]
各部分详解
状态跟踪决策的生命周期。典型流程是:已提议 -> 已接受。如果情况变化,ADR变为已弃用或被更新的ADR取代。重要的是,你永远不应该删除ADR——即使被拒绝的决策也有价值,因为它们防止未来的团队重新考虑已经评估过的选项。
日期确定决策的时间。这提供了时间背景:"我们在2024年系统规模为X时选择了这项技术。现在规模是10倍了,我们应该重新审视。"
背景是最重要的部分。写得好的背景部分给未来读者足够的信息来理解_为什么_这个决策在当时是合理的,即使情况后来发生了变化。包含具体数字("我们每天处理5万个订单")、约束("必须符合PCI-DSS")和团队因素("三名工程师有PostgreSQL经验,没人有MongoDB经验")。
决策应该是明确的。"我们将使用PostgreSQL 16作为订单服务的主数据存储"是好的。"我们可能应该考虑关系数据库"不是ADR——它是一个建议。
考虑的替代方案是长期最节省时间的部分。当新工程师问"为什么我们不用MongoDB?"时,答案就在这里。当团队一年后重新审视决策时,他们可以看到评估了什么以及为什么被拒绝。没有这部分,团队会无休止地重复同样的辩论。
后果关乎知识诚实。每个决策都有权衡。承认缺点建立信任,帮助未来的团队理解什么情况下可能需要重新审视决策。
完整的ADR示例
以下是一个展示模板实际应用的真实ADR:
# ADR-0023: 使用Apache Kafka进行服务间通信
## 状态
已接受
## 日期
2025-11-15
## 背景
我们的平台在过去一年从3个微服务增长到12个。
服务目前通过HTTP REST调用同步通信。这造成了几个问题:
- 级联故障:当库存服务宕机时,订单服务无法处理订单,
即使库存检查可以是最终一致的。
- 紧耦合:服务需要知道彼此的API契约和端点。
- 性能瓶颈:某些操作触发4-5个同步调用链,增加了延迟。
我们每天处理大约10万个事件。预计12个月内增长到50万。
团队有8名后端工程师,其中2人有Kafka经验。
## 决策
我们将采用Apache Kafka作为异步服务间通信的主要机制。
同步HTTP将保留用于调用方需要即时结果的请求/响应模式
(例如认证检查)。
我们将使用Confluent Cloud作为托管Kafka提供商,
以最小化运维开销。
## 考虑的替代方案
### RabbitMQ
- 优点:更简单的运维,更低的学习曲线,支持多种
消息模式(发布/订阅、点对点、路由)。
- 缺点:不太适合我们计划采用的事件溯源模式。
重放/回溯能力较弱。社区动力已转向Kafka
用于事件驱动架构。
- 拒绝原因:我们预计需要事件重放用于审计和调试。
Kafka的基于日志的架构更适合。
### AWS SQS + SNS
- 优点:完全托管,无需维护基础设施,与AWS紧密集成。
- 缺点:对AWS的供应商锁定。消息排序保证有限。
没有内置的流处理(需要Kinesis或Lambda)。
在我们预期的量级下每消息成本更高。
- 拒绝原因:我们希望避免加深AWS锁定,
并且需要金融事件的有序消息传递。
### 保持同步HTTP(添加断路器)
- 优点:无需新基础设施。团队已熟悉。
断路器解决级联故障。
- 缺点:不解决紧耦合。延迟仍在调用链中累积。
断路器是创可贴,不是解决根本耦合问题的方案。
- 拒绝原因:解决症状,而非根因。
## 后果
### 积极方面
- 服务解耦:生产者不需要知道消费者。
- 异步工作流消除级联故障。
- 事件重放实现强大的调试和审计能力。
- 为未来服务的事件溯源模式奠定基础。
### 消极方面
- 运维复杂性增加(Kafka集群、Schema Registry、
消费者组管理)。通过使用Confluent Cloud缓解。
- 团队需要Kafka概念和模式的培训。
- 异步流程中最终一致性替代强一致性。
某些工作流需要重新设计。
- 调试分布式异步流程比追踪同步HTTP调用更难。
我们需要分布式追踪(见ADR-0024)。
### 风险
- 如果消息量超过Confluent Cloud定价层级,
成本可能显著增加。监控并设置告警。
- 跨服务的Schema演进需要纪律。
计划采用Avro与Schema Registry。
## 相关决策
- ADR-0024: 采用OpenTelemetry进行分布式追踪
- ADR-0018: 服务通信契约(被本ADR取代)
这个ADR大约400字。写起来可能花了20分钟。它将在未来几年节省数小时的会议时间。
何时写ADR
并非每个技术选择都需要ADR。在两个JSON库之间选择不值得写一个。但某些决策绝对值得。
应该写ADR的情况:
- 决策影响系统结构。 任何改变服务通信方式、数据存储位置或组件组织方式的决策。
- 决策逆转成本高昂。 选择数据库、消息系统或部署平台。如果撤销这个决策需要超过一个冲刺,记录为什么这样决定。
- 存在多个可行选项。 如果选择显而易见,不需要ADR。如果你在三个选项之间辩论,记录推理过程。
- 决策跨越团队边界。 任何影响其他团队的架构选择都应该记录,以便他们理解原因。
- 未来的你可能质疑这个选择。 如果有任何可能某人(包括未来的你)会问"我们为什么这样做?",现在就写下来。
不需要写ADR的情况:
- 选择遵循既定的团队标准(例如,"我们总是使用Go做后端服务"不需要为每个服务写新ADR)
- 决策可以轻易逆转(重命名变量、选择代码格式化工具)
- 决策纯粹是外观上的(代码风格、命名约定——这些属于风格指南)
ADR生命周期和治理
生命周期
ADR有自然的生命周期:
- 草稿/已提议:有人识别出需要做出的决策并写了ADR草稿。团队讨论。
- 已接受:团队就决策达成一致。ADR状态变为已接受。
- 生效中:决策生效。ADR作为参考文档。
- 已取代:新决策替代了这个。旧ADR获得"被ADR-XXXX取代"的状态。新ADR引用旧的。
- 已弃用:决策不再相关(也许它适用的系统已退役)。
重要的是:永远不要删除ADR。即使已弃用或被取代的也有价值。它们构成了架构演进的时间线。
编号
使用顺序编号:ADR-0001、ADR-0002等。编号提供了时间顺序,使ADR在对话中容易引用:"正如ADR-42中决定的。"
有些团队使用基于日期的编号(ADR-2026-03-27-kafka),但顺序编号更简单且更广泛使用。
审查节奏
设置季度提醒来审查你的ADR:
- 是否有已接受的决策不再有效?
- 约束条件是否变化,使假设失效?
- 是否有团队非正式做出的决策应该被记录?
这种审查防止ADR腐化,保持集合的时效性。
在哪里存储ADR
最常见的方法,按推荐排序:
在代码仓库中
将ADR存放在仓库中的docs/adr/或docs/decisions/。这是最流行的方法,理由充分:
- ADR与它们描述的代码一起版本控制
- 它们出现在PR和代码审查中
- 它们不会在没人访问的wiki中成为孤儿
- 它们可通过
grep或搜索发现
惯例:每个ADR一个markdown文件,以编号和slug命名:
docs/adr/
0001-use-postgresql-for-orders.md
0002-adopt-event-driven-architecture.md
0003-monorepo-for-frontend.md
在专用架构工具中
像Archyl这样的工具提供内置ADR管理,具有额外功能:
- 将ADR直接链接到C4模型元素(容器、组件、关系)
- 跨所有项目搜索和过滤ADR
- 跟踪ADR状态和生命周期
- 将ADR连接到执行决策的合规规则
架构工具的关键优势是上下文。当开发者查看C4图中的PostgreSQL容器时,他们可以点击进入解释为什么选择PostgreSQL的ADR。决策和架构是连接的,而不是孤立的。
在Wiki中(不推荐)
Wiki(Confluence、Notion等)是ADR被遗忘的地方。它们与代码断开连接,难以发现,且很少维护。如果必须使用wiki,至少从代码仓库交叉引用ADR。
将ADR集成到工作流中
将ADR作为PR的一部分
对于重要的架构变更,要求ADR作为PR的一部分。在PR模板中添加一个部分:
## 架构影响
- [ ] 无架构变更
- [ ] ADR已创建/更新:[链接]
这使ADR创建常态化,确保决策在做出时就被记录,而不是几周后上下文已经模糊时才记录。
在架构评审中使用ADR
在架构评审或设计讨论中引用现有ADR。"在我们重新设计之前,先看看ADR-15怎么说的,为什么我们选择了这种方法。"这给了ADR可见性,强化了记录决策的习惯。
将ADR连接到架构图
这是ADR变得真正强大的地方。当ADR链接到C4模型中的特定元素时,它创建了一个可导航的知识图谱:
- 点击容器图中的Kafka容器 -> 看到ADR-0023解释为什么选择Kafka
- 点击PostgreSQL数据库 -> 看到ADR-0001解释数据库选型
- 点击API网关 -> 看到ADR-0012解释为什么选择Kong而不是自定义路由
在Archyl中,你可以将ADR链接到任何C4元素——系统、容器、组件或关系。这种连接意味着架构决策不是浮动的文档;它们锚定在它们影响的架构的特定部分上。
尽可能自动化
几种工具可以减少ADR管理的摩擦:
- adr-tools(Nat Pryce开发):用于在仓库中创建和管理ADR的命令行工具。运行
adr new "Use Kafka for messaging"从模板生成新ADR。 - Log4brains:从ADR markdown文件生成可搜索、可浏览的网站。
- Archyl:管理ADR并支持链接到C4元素、状态跟踪和跨项目搜索。
高级ADR实践
轻量级ADR(LADR)
有些团队觉得标准模板都太重。轻量级ADR只需三句话:
在[情况]的背景下,
我们决定[决策],
以实现[目标],
接受[权衡]。
示例:
在需要能容忍服务宕机的服务间通信的背景下,
我们决定通过Confluent Cloud使用Apache Kafka,
以实现服务解耦和事件重放能力,
接受增加的运维复杂性和最终一致性。
这种格式适合那些重要到需要记录但不值得写完整ADR的决策。
决策日志
决策日志是一份单一文档,按时间顺序列出所有ADR及其一行摘要。它用作索引:
| # | 日期 | 决策 | 状态 |
|---|---|---|---|
| 23 | 2025-11-15 | 使用Kafka进行服务间通信 | 已接受 |
| 24 | 2025-11-20 | 采用OpenTelemetry进行分布式追踪 | 已接受 |
| 25 | 2025-12-01 | 将内部API从REST迁移到gRPC | 已提议 |
这提供了所有决策的快速概览,无需打开单个ADR文件。
非技术决策的ADR
ADR不限于技术选择。团队用它们来记录:
- 流程决策:"我们将对安全关键代码进行结对编程"
- 组织决策:"平台团队拥有所有基础设施容器"
- 供应商决策:"我们将使用Datadog进行监控,而不是自建"
如果决策重要且逆转成本高,无论是否严格属于"架构",ADR都是合适的。
常见陷阱
事后才写ADR
写ADR的最佳时机是在决策过程中。几周后再写意味着你已经忘记了细微差别、考虑过的替代方案和当时存在的约束。让ADR编写成为决策过程的一部分,而不是事后补充。
写得太长
如果你的ADR超过一页,你可能在记录多个决策(拆分它们)或包含实现细节(留给设计文档)。简洁的纪律迫使清晰。
不记录被拒绝的替代方案
"考虑的替代方案"部分是ADR长期最有价值的部分。没有它,未来的工程师会提出相同的替代方案,不知道它们已经被评估和拒绝。务必解释你没有选择什么以及为什么。
放弃这个实践
一两个ADR不会提供太多价值。经过两年积累的50个ADR成为无价的知识库。忙碌时停止写ADR的诱惑很大。要抵制它。你花15分钟写的ADR将在以后节省数小时的会议时间。
不审查或更新状态
一个半数决策已过时的ADR集合是误导性的。安排季度审查。标记被取代的决策。保持集合的可信度。
为什么ADR在AI时代更重要
随着AI代理成为软件开发的积极参与者,ADR有了新的重要性。编写代码的AI代理可以读取你的ADR并理解:
- 为什么选择了某些技术(应该继续使用)
- 什么替代方案被拒绝了(不应该重新提出)
- 存在什么约束(合规要求、性能阈值)
- 接受了什么权衡(最终一致性、供应商依赖)
通过Archyl的MCP服务器,Claude Code等AI代理可以在提出架构建议之前访问你的ADR。代理不会为已经记录了为什么选择PostgreSQL的服务提议MongoDB——或者在认为决策应该被重新审视时明确标记。
这使ADR不仅是人类的文档,也是AI的护栏。
今天就开始
如果你的团队还没有使用ADR,从这三个步骤开始:
选择一个最近的决策。 想想你的团队在上个月争论过的一个架构选择。使用上面的模板为它写一个ADR。应该需要15-20分钟。
存储在仓库中。 创建一个
docs/adr/目录并添加文件。在你的下一个PR中包含它,让团队看到。养成习惯。 在PR模板中添加"架构影响"复选框。在下次架构会议中讨论ADR。一旦团队看到价值,实践就会建立起来。
下次有人问"我们为什么那样做?"时——他们一定会问的——你就有了答案。
探索更多关于架构文档的内容:什么是C4模型? | ADR最佳实践 | AI驱动的架构发现。或者免费试用Archyl,管理直接链接到C4模型的ADR。