ADR 架构决策记录实战:让技术选型可追溯可复盘

当团队问起”当年为什么选 RabbitMQ 而不是 Kafka”,往往没人答得上来——当事人离职、聊天记录淹没、文档里只有结论没有理由。ADR(架构决策记录,Architecture Decision Record)就是为这个痛点而生的轻量实践:用一篇几百字的短文档,把”在什么背景下、面对哪些选项、最终选了什么、放弃了什么、会带来什么后果”固定下来。它不追求完美,只追求可追溯、可复盘。

一、ADR 解决什么痛点

技术决策最大的成本不是”选错”,而是”忘记为啥这么选”。没有记录,半年后的你会重提已经被否决的方案,新同事会重复踩坑,复盘时只能拍脑袋。ADR 把决策从散落的聊天记录和会议里抽出来,沉淀成仓库里可检索、可 diff、可评审的文档。

举个真实到扎心的例子:某团队两年前把单体拆微服务,却保留了一个所有服务共享的”大库”。后来新人想按服务分库,被老人一句”当初试过不行”挡了回去,但没人说得清”不行”具体指什么、当时的流量和数据量又是多少。如果当年留一篇 ADR 写明”共享库是过渡方案,待订单量过 X 即拆分”,今天的分库决策就有了依据,而不是一句模糊的”不行”。

它和技术选型决策实战里讲的”决策矩阵”是互补关系:决策矩阵帮你把选项摆清楚、量化打分;ADR 则负责把”最终拍板”这件事及其理由写进历史。两者配合,选型才既有过程又有结论。

二、一个能直接抄的 ADR 模板

ADR 的核心字段就五个:状态、背景、决策、后果、备选。下面是一份 MADR 风格的最小模板,一篇通常控制在 200–500 字,别写成设计文档。

# ADR-0012 订单服务引入本地缓存

## 状态
Accepted(已采纳)

## 背景
订单详情接口 P99 达 800ms,数据库 CPU 持续 70%+。
我们需要在"加缓存"和"加只读副本"之间做选择。

## 决策
引入 Redis 本地二级缓存(Cache-Aside 模式),
TTL 60s,热点 key 手动预热。

## 后果
- 好:P99 降到 120ms,DB CPU 降到 35%。
- 坏:出现短暂数据不一致窗口(≤60s),需在文档标注。
- 风险:大 key 风险,另见缓存治理规范。

## 备选(被否决)
- 只读副本:成本更高,且无法缓解热点行争用。
- 不缓存:性能不达标,已排除。

三、ADR 的生命周期:不是一次性的

ADR 是有寿命的。一个决策今天成立,明天可能因为技术债或业务变化被推翻。用状态字段管理它的生命周期,比删文档更诚实:

状态含义何时用
Proposed提议中刚起草,等评审
Accepted已采纳评审通过,开始执行
Deprecated已弃用仍有效但不推荐新场景用
Superseded已被取代被新 ADR 推翻,注明替代编号

关键规则:被推翻的 ADR 不要删除,而是把状态改成 Superseded,并在开头加一行”被 ADR-00XX 取代”。这样后人能完整看到演进链路,而不是只看到”现在的正确答案”。

一个典型的演进链路长这样:ADR-0003 在创业初期决定”单体 + 单库”以换速度(Accepted);ADR-0019 业务增长后决定”订单服务独立分库”(Superseded 0003);ADR-0041 又因合规要求把订单库迁到独立集群(Superseded 0019)。任何人翻开 0003,一眼就能顺藤摸到今天的架构,以及每一次转折的理由。这条链比任何口头传承都可靠。

四、把 ADR 接进你的工作流

ADR 最怕”建了目录没人写”。把它接进现有流程最稳:重大选型先写 Proposed 版,随技术方案评审实战的评审单一起过,评审通过即翻成 Accepted。下面这个小脚本能一键生成带编号和日期的骨架,降低书写门槛。

#!/usr/bin/env python3
# adr_new.py — 生成一篇 ADR 骨架
import datetime, pathlib, sys

seq = len(list(pathlib.Path("docs/adr").glob("adr-*.md"))) + 1
num = f"adr-{seq:04d}"
today = datetime.date.today().isoformat()
title = sys.argv[1] if len(sys.argv) > 1 else "待定决策"

tpl = f"""# {num} {title}

## 状态
Proposed

## 背景

## 决策

## 后果

## 备选(被否决)
"""
pathlib.Path(f"docs/adr/{num}.md").write_text(tpl, encoding="utf-8")
print(f"created docs/adr/{num}.md ({today})")

五、常见踩坑与反模式

落地 ADR 时最容易出现这几种变形,提前识别能省很多返工:

其中最隐蔽的是”只记结论不记背景”——很多团队写 ADR 像写公告:”经讨论,决定采用 Kafka”。半年后背景早已变化,但文档看不出前提,于是没人敢动它,它反而成了僵化的枷锁。记住:没有背景的决策记录,价值减半;没有备选(被否决项)的记录,等于没做权衡。

  • 写成设计文档:ADR 只记录”决策与理由”,实现细节留给设计文档,别混在一起。
  • 只记结论不记背景:没有背景,后人无法判断前提是否还成立。
  • 不及时翻状态:决策变了状态还停在 Accepted,反而会误导。
  • 藏在 wiki 里:最好和代码同仓库,PR 里就能看到、能评审、能 diff。

六、与方案评审、复盘怎么配合

ADR 不是孤立的。上游它承接技术选型决策的取舍分析,评审环节它随方案评审单一起过,下游它又是技术复盘(Postmortem)的输入——当一次故障源于某个旧决策,直接翻出对应 ADR,就能看清”当时为什么这么选”,复盘才不会变成甩锅会。三者串起来,团队的工程判断力就沉淀成了可继承的资产。

七、落地清单:今天就能开始

  1. 在仓库建 docs/adr/ 目录,放一份 README 说明模板与状态含义。
  2. 挑一个近期真实决策(如”为什么换日志框架”)补成第一篇 ADR。
  3. 把”重大选型须附 ADR”写进团队的方案评审 checklist。
  4. 每季度扫一遍 Accepted 的 ADR,过期的翻 Deprecated / Superseded。

ADR 的价值不在写出来那一瞬,而在半年后有人翻到它、少走一次弯路的那一刻。从今天这篇开始,让团队的每个关键架构决策都”说得清来历”。

如果团队规模小、决策少,也不必追求仪式感——每周挑一个”这周我们拍了什么板”写下来就够。ADR 是工具不是负担,它的唯一 KPI 是:当有人问”当初为啥这么选”时,你能三秒内指着一篇文档给出答案。

上一篇 Redis 大 key 与热 key 排查实战:从发现到根治
下一篇 磁盘 inode 耗尽排查实战:服务诡异故障的根因定位