附录:ADR 模板与示例
TL;DR:本篇提供 ADR(架构决策记录)的空白模板与一个公认写得好的完整示例。模板遵循 Michael Nygard 的原始四要素结构,示例改编自业界广泛参考的微服务缓存决策案例。直接复制模板使用,对照示例检查完整度——一篇合格的 ADR 不在于篇幅,而在于假设可证伪、后果含负向、放弃了什么被写明。
概念解释
ADR 模板的来源
Michael Nygard 在 2011 年的文章《Documenting Architecture Decisions》中提出的格式极其简洁:Context(上下文)、Decision(决策)、Consequences(后果)。社区后续扩展出多种变体(MADR、Y-Statements),但 Nygard 原版因其极低的上手成本,仍是业界最广泛使用的格式。本模板在原版基础上增加**假设(Assumptions)**字段——这是本专栏的核心深化,因为假设是触发重新决策的钩子。
怎样算一篇写得好的 ADR
| 维度 | 好的 ADR | 差的 ADR |
|---|---|---|
| 上下文 | 写明面临的约束、团队现状、业务量级 | 只写"需要选一个缓存" |
| 决策 | 写明选了什么、放弃了什么 | 只写"选 Redis" |
| 后果 | 正向和负向都写 | 只写优点 |
| 假设 | 可证伪,有触发重评的信号 | 无假设或假设藏在脑里 |
| 篇幅 | 1-2 页 | 10 页文档或半句话 |
目录
| 章节 | 说明 |
|---|---|
| ADR 空白模板 | 直接复制使用 |
| 完整示例:缓存方案选型 | 公认写得好的参考案例 |
| 模板变体说明 | MADR、Y-Statements 简介 |
| 使用建议 | 何时写、放哪、怎么演进 |
ADR 空白模板
# ADR-{编号}: {决策标题}
**状态**: Proposed | Accepted | Deprecated | Superseded by ADR-{编号}
**日期**: YYYY-MM-DD
**决策者**: {负责人}
## 上下文
{面临的工程问题、约束、团队现状、业务量级、时间尺度。
回答:为什么现在需要做这个决策?有什么不可妥协的约束?}
## 决策
{选定方案。一句话说清选了什么。
补充:放弃了哪些候选方案?各自放弃了什么?}
## 后果
### 正向
- {选这个方案带来的好处}
### 负向
- {选这个方案带来的代价、风险、技术债}
## 假设
| 假设 | 可证伪信号(出现时重评) |
|------|--------------------------|
| {前提1} | {若观察到X则假设不成立} |
| {前提2} | {若观察到Y则假设不成立} |
## 退出条件
| 信号 | 预设行动 |
|------|----------|
| {可观察的阈值} | {触发后做什么} |
完整示例:缓存方案选型
以下是一个公认写得好的 ADR 示例,改编自微服务缓存选型的常见工程场景。
# ADR-007: 订单服务选用 Redis 主从版作为缓存
**状态**: Accepted
**日期**: 2026-01-15
**决策者**: 订单服务团队
## 上下文
订单服务 QPS 峰值 5w,读多写少(读写比约 10:1)。
当前使用本地缓存(Caffeine),存在以下问题:
1. 多实例间缓存不一致,导致超卖风险
2. 实例重启后缓存击穿,回源 DB 压力骤增
3. 无法支持跨实例的缓存预热
团队有 Redis 运维经验(哨兵模式),但无 Cluster 运维经验。
预算约束:年缓存成本不超过 X 万。
时间约束:需在 Q2 前完成迁移。
## 决策
选用 Redis 主从版(Sentinel 高可用),非 Cluster。
读穿透使用布隆过滤防止缓存穿透。
放弃的候选方案:
- Redis Cluster:团队无运维经验,迁移风险高,Q2 前无法稳妥上线
- Memcached:无持久化,重启后需全量预热,与超卖风险场景冲突
- 继续用本地缓存:多实例不一致问题无法解决
## 后果
### 正向
- 多实例缓存一致,消除超卖风险
- 哨兵自动故障切换,减少人工介入
- 团队已有哨兵运维经验,上手快
### 负向
- 单点写入瓶颈:写入 QPS 上限约 10w,若业务增长超 3 倍需迁移 Cluster
- 内存成本:预估 2 年内数据量约 50G,需预留 70% 水位
- 增加了一个运维组件,故障域扩大
## 假设
| 假设 | 可证伪信号 |
|------|------------|
| 订单数据量两年内不超 50G | Redis used_memory 超过 35G(70% 水位)|
| QPS 增长不超 3 倍(峰值不超 15w)| 单日峰值 QPS 连续 7 天超过 12w |
| 团队能在一个月内掌握哨兵运维 | 上线后哨兵切换失败率超过 1% |
## 退出条件
| 信号 | 预设行动 |
|------|----------|
| used_memory 超过 35G | 启动迁移 Cluster 评估 |
| 写入 QPS 峰值超 12w | 评估分片或迁移 Cluster |
| 哨兵切换连续失败 2 次 | 回退本地缓存 + DB 兜底,排查哨兵配置 |
模板变体说明
| 变体 | 特点 | 适用场景 |
|---|---|---|
| Nygard 原版 | 上下文 + 决策 + 后果(3 要素) | 快速上手、轻量记录 |
| 本专栏扩展版 | + 假设 + 退出条件(5 要素) | 高影响或不可逆决策 |
| MADR | + 多属性评分(加权矩阵集成) | 多方案竞争、需量化比较 |
| Y-Statements | Why/What/How 三段式 | 架构全景梳理 |
建议:日常用 Nygard 原版或本专栏扩展版;高影响决策(数据库迁移、公开 API 契约)必须加假设和退出条件。
使用建议
何时写
- 必须写:单向门决策(不可逆)、影响多个团队的决策、公开 API 契约
- 建议写:技术选型、架构变更、数据迁移
- 不必写:低风险可逆决策(如函数命名、内部工具选型)
放在哪
- 放在代码仓库(推荐):随 git 历史可追溯,代码 review 流程天然覆盖决策评审
- 不放 Wiki/Confluence:会因迁移丢失,且与代码脱节
怎么演进
- 新决策推翻旧决策时,不删除旧 ADR,新建 ADR 并标注
Supersedes ADR-XXX - 旧 ADR 顶部标注
Superseded by ADR-YYY - 形成决策树,而非一堆孤立文件
练习
选一个你所在系统的技术决策,用本模板写一份 ADR。重点检查:假设是否可证伪?后果是否包含负向影响?放弃了哪些候选方案是否写明?若找不到当初的上下文,就把"现在能推断的"写明并标注"上下文为事后重建"。
关联笔记
- 决策记录(ADR):让技术决策可追溯可演进(ADR 的完整理论与实践)
- 决策与权衡总览:从选项到可审阅的承诺(决策三层结构中的"承诺"层)
- 可逆性与双向门:降低决策的不可逆成本(判断哪些决策值得写 ADR)
- Premortem 与预承诺:在行动前发现失败路径(ADR 的假设可由 Premortem 识别)
参考资料
评论 (0)