决策记录模板
ADR 的价值在于其结构化的格式。好的 ADR 模板让决策记录变得简单,团队不需要每次写 ADR 时都从头思考结构。但模板只是骨架,内容才是灵魂。
本文提供完整的 ADR 模板、不同场景下的模板变体,以及一个完整的填写示例。
完整 ADR 模板
---
title: ADR-XXXX: [决策标题]
description: [一句话描述决策内容]
---
# ADR-XXXX: [决策标题]
## 状态
[Proposed | Accepted | Deprecated | Superseded]
如果状态为 Deprecated 或 Superseded,补充说明原因和替代方案。
## 背景
[决策的问题场景和上下文]
描述:
- 这个问题是什么时候被发现的
- 当前的痛点是什么
- 有哪些约束条件
## 决策
[最终选择的方案]
描述:
- 选择什么
- 核心设计是什么
- 如何与其他系统交互
## 替代方案
[未被选择的备选方案]
对每个替代方案,说明:
- 方案是什么
- 为什么没有选择
- 主要的权衡点是什么
## 权衡
[我们愿意牺牲什么来换取什么]
描述:
- 选择这个方案的核心收益是什么
- 我们接受了这个方案的哪些代价
- 这些代价在什么条件下可以接受
## 后果
### 正面
[决策带来的收益]
列出具体、可衡量的收益:
- 性能提升了多少
- 成本降低了多少
- 团队效率提升了多少
### 负面
[决策带来的问题]
列出具体的问题和风险:
- 需要额外的运维能力
- 引入了哪些依赖
- 有哪些边界情况需要处理
### 待定
[尚未确定的因素]
列出:
- 需要进一步验证的假设
- 需要持续观察的指标
- 未来可能需要重新决策的点
## 相关 ADR
- [引用相关的其他 ADR]
## 历史
| 日期 | 版本 | 修改内容 |
| --- | --- | --- |
| YYYY-MM-DD | 1.0 | 初稿 |
| YYYY-MM-DD | 1.1 | [修改说明] |
模板字段说明
状态
状态是 ADR 中最重要的元数据,它告诉读者这个决策的当前状态。
Proposed 表示决策仍在评审中,尚未最终确定。提案阶段的 ADR 是开放的,欢迎团队成员提出意见。
Accepted 表示决策已通过评审,成为团队共识。已接受的 ADR 可以被质疑和重新评审,但推翻决策需要走完整流程。
Deprecated 表示决策的背景已发生变化,这个决策不再适用。已废弃的 ADR 保留作为历史参考。
Superseded 表示有新的决策替代了这个决策。需要在新的 ADR 中引用被替代的编号。
背景
背景部分要回答「为什么这个问题需要被解决」。不是描述问题是什么,而是描述问题的严重性和紧迫性。
好的背景示例:
当前支付系统使用单体架构,所有模块共享一个数据库。随着订单量增长(目前日均 10 万单,预计明年达到 50 万单),数据库已成为性能瓶颈:
- 高峰期订单创建接口 P99 延迟达到 500ms
- 数据库 CPU 使用率持续在 80% 以上
- 单次发布影响范围不可控,上周发布优惠券模块导致订单支付失败
不好的背景示例:
决策
决策部分要回答「我们选择了什么方案」。要具体,不要模糊。
具体不是说要把整个技术方案都写出来,而是说清楚选择本身。例如:
选择 Kafka 作为消息队列,使用单集群多主题的部署模式。
主题命名规范:{业务线}-{业务场景},如 payment-order-created
分区策略:按订单 ID 哈希分区,保证同一订单的消息有序
副本策略:3 副本,acks=all,保证消息不丢失
权衡
权衡部分是 ADR 的核心。很多 ADR 写得很详细,但缺少权衡部分,导致读者不知道「这个决策的代价是什么」。
权衡部分要明确回答三个问题:
- 核心收益:这个决策解决了什么问题
- 接受的代价:我们愿意牺牲什么
- 适用条件:这个权衡在什么条件下成立
例如:
选择 Kafka 的核心收益:
- 高吞吐量:支持每秒百万级消息处理,满足未来业务增长
- 持久化保证:消息至少消费一次,支持消息重试
接受的代价:
- 运维复杂度高:需要专业团队维护 Kafka 集群
- 延迟较高:Kafka 的端到端延迟在 10-100ms,不适合低延迟场景
适用条件:
- 日均消息量超过 100 万条
- 团队有 Kafka 运维经验或愿意投入学习
后果
后果部分要分正面、负面、待定三个维度。
正面后果要具体、可衡量:
### 正面
- 订单处理吞吐量提升 10 倍:从 1000 QPS 提升到 10000 QPS
- 高峰期数据库 CPU 使用率从 80% 降低到 30%
- 故障隔离能力提升:单个模块故障不影响其他模块
负面后果要诚实、不回避:
### 负面
- 系统复杂度增加:需要处理分布式事务、服务间通信问题
- 运维成本增加:需要维护多个服务实例和配置中心
- 调试难度增加:一个问题可能涉及多个服务的日志追踪
待定后果要明确需要观察的指标:
### 待定
- 如果单服务 QPS 超过 5000,需要评估水平扩展方案
- 如果消息积压严重,需要评估消费者扩容或分区调整
- 如果 Kafka 集群出现故障,需要验证故障恢复流程
不同类型决策的模板变体
架构选型
架构选型类 ADR 需要重点关注「为什么选择这个架构」和「架构的边界在哪里」:
## 决策
采用微服务架构,将系统拆分为以下服务:
- 用户服务:用户注册、登录、信息管理
- 订单服务:订单创建、查询、取消
- 支付服务:支付下单、回调处理、退款
服务间通信:
- 同步调用:使用 gRPC,服务间实时交互
- 异步通知:使用消息队列,服务间解耦
## 边界定义
微服务架构的边界:
- 每个服务独立数据库,不共享数据存储
- 服务间不允许跨库 join 查询
- 服务间通过 API 或消息交互,不直接操作其他服务的数据
## 放弃的边界
以下边界在当前阶段不执行:
- 服务独立部署频率:考虑到团队规模,当前所有服务使用同一 CI/CD 流水线
- 服务多语言:当前所有服务使用 Java,保持技术栈统一
组织流程
组织流程类 ADR 需要重点关注「流程的执行细节」和「责任归属」:
## 决策
实施技术方案评审流程(RFC + ADR):
- 所有影响系统架构的决策必须经过 RFC 提案阶段
- RFC 提案需要在技术评审会上讨论
- 评审通过后,RFC 转化为 ADR,记录在 /docs/adr/ 目录
## 责任归属
- 提案人:负责撰写 RFC,主导评审过程
- 评审人:技术负责人、相关模块负责人、测试负责人
- 决策者:技术负责人拥有最终决策权
## 执行计划
- 第一阶段(1 个月):在核心团队内试点,要求所有新服务的架构设计必须经过评审
- 第二阶段(3 个月):推广到全团队,形成流程规范
- 第三阶段(持续):持续优化评审流程,提高评审效率
技术标准
技术标准类 ADR 需要重点关注「标准的内容」和「标准的适用范围」:
## 决策
统一 API 设计规范:
### URL 规范
- 使用 kebab-case:`/user-profile`、`/order-list`
- 名词复数形式:`/users`、`/orders`
- 嵌套资源限制最多两层:`/users/{id}/orders`
### HTTP 方法
- GET:查询,不修改资源
- POST:创建新资源
- PUT:完整更新资源
- PATCH:部分更新资源
- DELETE:删除资源
### 响应格式
```json
{
"code": 0,
"message": "success",
"data": {}
}
适用范围
本标准适用于:
- 所有对外暴露的 RESTful API
- 所有服务间 gRPC 接口
本标准不适用于:
- 内部工具类 API(由工具团队自行定义)
- 实验性功能(可在正式上线前补充标准)
## 模板使用示例:选择消息队列
假设团队需要选择消息队列,当前有三个候选方案:Kafka、RabbitMQ、RocketMQ。
### 第一步:填写背景
```markdown
## 背景
订单系统在高峰期面临以下挑战:
1. 下单成功后需要发送多条通知(短信、邮件、站内信、积分系统)
2. 当前使用同步调用,导致下单接口 RT 高达 800ms
3. 未来订单量预计增长 5 倍,同步调用的扩展性不足
需要引入消息队列实现系统间解耦,将同步操作异步化。
第二步:列出替代方案
## 替代方案
### 方案 A:Kafka
- 优点:高吞吐、低延迟、生态成熟
- 缺点:运维复杂度高,需要专业团队;延迟在 10-100ms
- 适用场景:日志收集、大数据流处理、高吞吐场景
### 方案 B:RabbitMQ
- 优点:功能完善、支持多种协议、社区活跃
- 缺点:吞吐量相对较低(万级 QPS)
- 适用场景:中小规模应用、复杂路由场景
### 方案 C:RocketMQ
- 优点:阿里开源,金融级可靠性,Java 友好
- 缺点:社区规模相对较小,文档质量不如 Kafka
- 适用场景:电商交易、订单系统
第三步:说明权衡
## 权衡
**选择方案 B(RabbitMQ)的原因:**
- 团队现状:团队 5 人,无 Kafka 运维经验,学习成本高
- 业务规模:日均订单 10 万条,高峰期 QPS 约 1000,RabbitMQ 完全满足
- 运维能力:RabbitMQ 单机部署即可满足当前需求,运维简单
- 功能需求:需要消息重试、死信队列、延迟消息,RabbitMQ 插件支持完善
**放弃方案 A(Kafka)的原因:**
- Kafka 的优势(百万级吞吐)在当前业务规模下用不上
- Kafka 的运维复杂度远超团队当前能力
- 如果未来业务量增长 100 倍,可以重新评估迁移到 Kafka
**放弃方案 C(RocketMQ)的原因:**
- RocketMQ 文档质量不如 RabbitMQ
- 社区规模较小,问题解决依赖阿里团队
- 团队 Java 技术栈与 RabbitMQ 的 .NET 背景不冲突
第四步:填写后果
## 后果
### 正面
- 下单接口 RT 降低:从 800ms 降低到 200ms
- 系统耦合度降低:新增通知渠道不需要修改订单服务代码
- 削峰能力:高峰期消息积压,平峰期逐步消费
### 负面
- 引入新组件:RabbitMQ 需要额外的运维和监控
- 消息延迟:通知类消息有 1-5 秒延迟,用户感知到「下单成功但通知稍后才能收到」
- 调试复杂度:消息链路出问题需要额外的追踪能力
### 待定
- 如果单队列消息积压超过 10 万条,需要评估消费者扩容
- 如果 RabbitMQ 成为性能瓶颈,需要评估迁移到 Kafka
- 如果团队扩张到 10 人以上,需要评估 RabbitMQ 集群模式
总结
ADR 模板是骨架,内容是灵魂。使用模板不是为了机械地填空,而是为了让团队在记录决策时有统一的结构,便于后续查阅。
好的 ADR 有三个标准:
- 完整:背景、决策、权衡、后果都有
- 诚实:权衡和负面后果不回避
- 可操作:后果部分明确了下一步行动
模板是起点,不是终点。根据团队的实际需求,可以增删字段,找到最适合团队的结构。
下一节我们将介绍决策评审流程,讲解如何组织有效的技术评审。