决策记录模板

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 写得很详细,但缺少权衡部分,导致读者不知道「这个决策的代价是什么」。

权衡部分要明确回答三个问题:

  1. 核心收益:这个决策解决了什么问题
  2. 接受的代价:我们愿意牺牲什么
  3. 适用条件:这个权衡在什么条件下成立

例如:

选择 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 有三个标准:

  1. 完整:背景、决策、权衡、后果都有
  2. 诚实:权衡和负面后果不回避
  3. 可操作:后果部分明确了下一步行动

模板是起点,不是终点。根据团队的实际需求,可以增删字段,找到最适合团队的结构。

下一节我们将介绍决策评审流程,讲解如何组织有效的技术评审。