08 · 架构决策记录(ADR)与演进
上一章你学会了从 0 设计一个系统。但系统设计完不是终点——它会随业务长大、被后人接手、在你早忘了「当初为什么」的时候继续运行。这一章讲怎么让架构活得久、长得好。
开场:最先丢失的,永远是「为什么」
想象一个场景。你接手一个三年前别人设计的系统,翻开代码,看到一个奇怪的设计:订单数据被同时写进了两个不同的存储,中间还有套对账逻辑。你的第一反应是:「这不脱裤子放屁吗?写一个不就完了?」于是你大笔一挥,删掉一个、合并逻辑——
三周后线上炸了。原来当年之所以这么设计,是因为有个监管要求订单流水必须落进一个不可篡改的存储,而主库要支持高频改单,两个诉求无法在一个存储上同时满足。这个理由,代码里一个字都没写,当年拍板的人也早就离职了。
这就是架构最致命的信息流失:代码和图能告诉你系统「是什么」,但几乎永远讲不清「为什么这么选、当初放弃了什么」。 而恰恰是后者,才是接手的人最需要、也最容易丢失的东西。
口头说过的「咱们这么定是因为……」,会在三次会议、两次离职、一年时间之后,蒸发得一干二净。等需要它的时候,只剩下一个看起来莫名其妙的设计,和一群不敢动它的人。
这一章给的解药很简单,简单到很多人不屑于做——把重要的架构决策,用一页纸记下来。 这页纸叫 ADR。
一、ADR:给架构决策建一本「为什么」的账
ADR = Architecture Decision Record,架构决策记录。 它是一份轻量文档,每做一个重要架构决策,就写一份,记下这个决策的来龙去脉。
它的精髓全在「轻量」二字:
- 一个决策一份,通常一页纸,几分钟能读完。
- 追加,不修改:决策变了就写新的一份,把旧的标记为「被取代」——保留历史,而不是抹掉历史。因为「我们曾经这么想、后来为什么改了」本身就是宝贵信息。
- 和代码放在一起(比如仓库里一个
docs/adr/目录),让它随代码一起被版本管理、被搜索、被 review。
一个可以直接抄走的 ADR 模板
┌──────────────────────────────────────────────────────────┐
│ ADR-007:订单流水采用「主库 + 不可篡改日志」双写 │
├──────────────────────────────────────────────────────────┤
│ │
│ 状态:已采纳 │
│ (草稿 / 已采纳 / 已废弃 / 被 ADR-015 取代) │
│ │
│ 日期:2026-05-23 决策者:架构组 + 合规 │
│ │
│ ── 背景 / 上下文 ─────────────────────────────────── │
│ 监管要求订单流水必须落入「写后不可改」的存储以备审计; │
│ 但业务又要求订单可被高频修改(改地址、改数量)。 │
│ 单一存储无法同时满足「可改」与「不可篡改」。 │
│ │
│ ── 决策 ─────────────────────────────────────────── │
│ 主库存可变订单状态,服务高频读写; │
│ 同时把每次变更追加写入一份不可篡改日志,作为审计依据; │
│ 以日志为准做定期对账。 │
│ │
│ ── 考虑过的其它选项 ─────────────────────────────── │
│ A. 只用主库 + 软删除标记 → 否决:软删除仍可被改,过不了审计 │
│ B. 只用不可篡改存储 → 否决:无法支持改单,业务不可接受 │
│ C. 双写(本方案) → 采纳 │
│ │
│ ── 取舍与后果 ───────────────────────────────────── │
│ + 同时满足了合规与业务两个硬约束 │
│ − 引入双写,需要对账逻辑兜底两边不一致(已知复杂度) │
│ − 后人会觉得「多此一举」——本 ADR 就是写给你看的,别删! │
│ │
└──────────────────────────────────────────────────────────┘模板就六块,记牢这六个标题就够了:
| 字段 | 写什么 | 为什么不能省 |
|---|---|---|
| 标题 | 一句话说清这个决策是什么 | 方便日后检索 |
| 状态 | 草稿 / 已采纳 / 已废弃 / 被某 ADR 取代 | 让读者知道这条还算不算数 |
| 背景 / 上下文 | 当时面临什么问题、什么约束 | 后人最缺的就是「当时的处境」 |
| 决策 | 我们最终决定怎么做 | 「是什么」——这部分代码里也能看到 |
| 考虑过的其它选项 | 还想过哪些方案,为什么没选 | 「放弃了什么」是 ADR 独有的价值 |
| 取舍与后果 | 这个选择带来什么好处、什么代价、什么已知债务 | 让后人清楚地继承「这是有意的取舍」 |
注意上一章的呼应:你在 07 的步骤 ⑧ 里,被反复要求说出「我选了 A、放弃了 B、因为……」和「未决问题」。那些话,逐字逐句就是一份 ADR 的『决策 + 其它选项 + 取舍』。 ADR 不是额外的负担,它只是让你把本来就该想清楚的东西,落在纸上别让它跑了。
二、强观点:记「为什么」比记「是什么」重要一百倍
这是这一章、甚至整个仓库最想让你记住的一条原则,值得单独拎出来反复说:
代码、架构图、API 文档,记录的全是「是什么」——系统现在长这样、有这些模块、这样调用。这些东西,只要你愿意,随时能从系统本身「读」出来。
但「为什么是这样、当初放弃了什么、在拿什么换什么」——这是『读』不出来的。它只活在做决定那个人的脑子里,不写下来,就随风而逝。
为什么「为什么」这么金贵?三个理由:
- 「是什么」会自我证明,「为什么」不会。 看代码就知道系统是什么样;但代码永远不会告诉你「我们本来想用更简单的方案 B,是因为某个约束才忍痛上了复杂的方案 C」。
- 没有「为什么」,后人只能在两种错误里二选一:要么不敢动(「这设计看着怪,但谁知道动了会不会出事」,于是系统僵化),要么乱动(像开场那个删双写的例子,直接踩雷)。有了「为什么」,后人才能安全地判断「这条约束还在不在,这个决策还成不成立」。
- 「为什么」是可迁移的智慧,「是什么」只是一次性的结果。 一份好 ADR 里「我们为了 A 牺牲了 B」的推理,换个项目、换个系统,后人还能复用那套权衡思路——这正是本仓库一以贯之的信念。
💡 一句话立此原则:好的文档不解释「代码做了什么」(代码自己会说),好的文档解释「代码为什么这么做」(代码永远不会说)。 把记录的力气,花在那些一旦丢失就再也找不回来的东西上。
这也正是 根 README 的三条阅读原则 里第一条的延伸:「先问『为什么』,再看『怎么做』。看不到取舍,就等于没看懂。」——ADR,就是把这条原则从「读」变成「写」。
三、演进式架构:别想一次设计到位,要留好「接缝」
07 反复讲「架构是迭代出来的」。但**「能迭代」本身,是要设计的**——一个糟糕的架构,改一点就牵一发动全身,根本迭代不动。
演进式架构(Evolutionary Architecture) 的核心思想是:
既然你注定无法一次设计对,那就把架构设计成「容易改对」的样子。 重点不是「现在完美」,而是「将来能低成本地替换某一块」。
怎么做到「容易改对」?答案是留好「接缝」(seam)——也就是清晰的模块边界和接口。一个有好接缝的系统,长这样:
没有接缝(一团泥): 留好接缝(模块化):
┌─────────────────────┐ ┌────────┐ 接口 ┌────────┐
│ 所有逻辑揉成一坨 │ │ 模块 A │◀─────▶│ 模块 B │
│ 支付、库存、通知 │ └────────┘ └───┬────┘
│ 互相直接调来调去 │ │ 接口
│ 改一处,处处地震 │ ┌────────┐ 接口 ┌──▼─────┐
└─────────────────────┘ │ 模块 D │◀─────▶│ 模块 C │
└────────┘ └────────┘
想换掉「通知」? 想换掉模块 C?
→ 不可能,它和谁都缠在一起 → 拔掉它、换一个,只要接口不变,
其它模块毫无感觉接缝的价值,在于把「将来的不确定」隔离开。 举几个上一章短链服务的例子:
- 你把「短码生成」做成一个有清晰接口的独立模块。第一版用「预批发号段」,将来如果想换成别的生成策略,只换这一块,跳转服务和存储毫不知情。
- 你把「缓存」藏在跳转服务的接口背后。第一版用进程内缓存,涨上去了换成 CDN——对调用方是透明的。
这正是每个模板「第 12 节 · 演进路线」在传递的智慧:AI 对话产品的演进路线 里,MVP 阶段直接调外部模型 API,成长期才换成自建推理 + 连续批处理——之所以换得动,正因为「调用模型」这件事一开始就被收在一个清晰的边界后面。好的接缝,让演进从『重写』变成『替换』。
💡 设计时多问一句:「这一块,将来最可能被换掉吗?」 最可能变的地方,就要用最干净的接口把它「包」起来,给它留好接缝。这是用「现在多想一点」换「将来少痛一片」。
四、技术债:它不全是坏事,关键是「记账」
「技术债」这个词常被当成纯贬义——好像欠了债就是干了坏事。这是个误解。
技术债的真正含义,借自金融:为了「现在更快地拿到价值」,而有意识地选了一个将来需要偿还的权宜方案。 就像借钱办事——借钱本身不是错,关键是你知不知道自己借了、打不打算还。
技术债分两种,要分清:
┌───────────────────────────┬───────────────────────────┐
│ 有意识的债(健康) │ 无意识的债(危险) │
├───────────────────────────┼───────────────────────────┤
│ 「为了赶上发布,这版先用 │ 「我也不知道为什么这里这么 │
│ 最简单的方案,记 TODO, │ 写,反正能跑」 │
│ 下季度重构」 │ │
│ │ │
│ → 是理性的商业权衡 │ → 是失控,你甚至不知道欠了债 │
│ → 写进 ADR / backlog,排期还 │ → 哪天突然爆雷,完全没准备 │
└───────────────────────────┴───────────────────────────┘对一个 MVP 来说,故意欠技术债往往是正确的——07 里短链服务第一版「单存储 + 单机发号」就是一笔债,但它换来了「快速上线、验证需求」,完全划算。错的不是欠债,而是:
- 不知道自己欠了债(无意识的债,最危险);
- 欠了债不记录(还是会忘「为什么」,回到本章开篇那个坑);
- 永远只借不还(债越滚越大,直到某天系统改不动了)。
处理技术债的纪律就三条:① 有意识地借(明确这是权宜之计);② 把它记下来(一笔技术债,就是一条该写的 ADR——背景是「为什么暂时这么凑合」);③ 安排偿还时机(挂进 backlog,定个「当 X 发生时就还」的触发条件,而不是「有空再说」)。
把技术债当成「记在账上、按计划偿还的负债」来管理,而不是「藏在地毯下、假装不存在的脏东西」——这是成熟团队和草台班子的分水岭。
五、康威定律:你的架构,会长得像你的组织
聊架构演进,绕不开一条「定律」。它不是技术规律,而是社会学观察,却深刻影响每一个系统的最终形态:
康威定律(Conway's Law):系统的架构,会不可避免地长得像设计它的那个组织的沟通结构。
翻成人话:两个模块之间的接口,长得像这两个模块背后那两拨人之间的沟通方式。 如果两个团队天天开会、坐在一起,他们做出来的两个模块大概率紧密耦合;如果两个团队分属不同部门、只能靠邮件交流,他们的模块之间就会自然形成清晰(甚至僵硬)的边界。
组织长这样: 系统就会长成这样:
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ 支付团队 │ │ 物流团队 │ ───▶ │ 支付服务 │ │ 物流服务 │
│ (各自独立)│ │ (各自独立)│ │ (独立部署)│ │ (独立部署)│
└─────────┘ └─────────┘ └────┬────┘ └────┬────┘
几乎不沟通,各管各的 └─ 接口清晰、松耦合 ─┘
┌───────────────────────┐ ┌───────────────────────┐
│ 一个大团队啥都管 │ ───▶ │ 一个大单体,啥都揉一起 │
└───────────────────────┘ └───────────────────────┘康威定律对架构演进有两条极其重要的启示:
- 拆服务之前,先看团队结构。 你想把单体拆成微服务(04 讲过微服务),但如果团队还是一锅粥、谁都管所有事,那拆出来的「微服务」之间照样紧耦合、改一个动一片——架构边界和团队边界对不齐,拆了也是白拆。 想要松耦合的服务,先有边界清晰、能独立负责的团队。
- 反过来用它(逆康威操作):想要什么样的架构,就先组织成什么样的团队。 你希望系统由几个独立演进的服务组成?那就先按这些服务把人分成几个能独立决策的小队,架构会自然朝你要的方向长。组织设计,本身就是一种架构设计。
💡 康威定律的本质提醒:架构问题,常常是组织问题伪装的。 当你发现两个模块怎么解耦都解不干净,先别急着改代码——去看看是不是背后那两拨人本就分不开。很多「架构难题」,真正的解法在组织结构里,不在技术里。
六、什么时候该重构 / 该升级架构?(用判断,不用感觉)
系统在长大,你迟早面临这个问题:现在这套架构,该不该动了? 新手常凭两种糟糕的依据做决定:
- 凭感觉:「这代码看着真丑,重构吧」——丑不是升级架构的理由,不影响任何质量属性的丑,可以先忍。
- 跟风:「大厂都上微服务/中台/某新架构了,我们也上」——别人的瓶颈不是你的瓶颈,照搬大厂成熟期的架构套自己的成长期,是头号过度设计。
那靠什么判断?靠前六章学的两把尺子:瓶颈和质量属性。
该不该升级架构?——拿这两把尺子量,而不是拍脑袋:
┌────────────────────────────────────────────────────────┐
│ 尺子一:瓶颈(来自 06 + 07 步骤⑦) │
│ 现在,某个质量属性是不是已经被架构卡住了? │
│ • 数据库读到冒烟、加再多缓存也压不住了? │
│ • 一个模块改动总是连累一片、发布越来越慢? │
│ → 真实的、量出来的瓶颈,才是升级的信号 │
├────────────────────────────────────────────────────────┤
│ 尺子二:质量属性的变化(来自 06) │
│ 业务对「要多好」的要求,是不是上了一个台阶? │
│ • 可用性要求从 99% 提到 99.99%? │
│ • 用户量/数据量进入了下一个量级(07 估算说的「100倍」)?│
│ → 质量目标变了,旧架构撑不住了,才升级 │
└────────────────────────────────────────────────────────┘把判断流程固化成一句话:
不是「这架构旧了/丑了」就升级,而是「某个我真正在乎的质量属性,被当前架构卡住了,且这个卡点是真实存在(量出来)的瓶颈」才升级。 升级架构是有巨大成本和风险的动作,它必须是为了换取某个具体的、当下需要的质量属性——这正是 06 质量属性与取舍 教你的判断方式。
而且,每一次「决定升级架构」本身,就是一个该写 ADR 的重大决策:背景(什么瓶颈逼我们动)、决策(升成什么)、其它选项(还能怎么扛)、取舍(为这次升级我们付出什么)。——你看,本章所有的工具,最后都串回了 ADR。
本章小结
- 最先丢失的永远是「为什么」:代码和图能说清系统「是什么」,但讲不清「为什么这么选、放弃了什么」——而后者才是后人最需要、最易蒸发的东西。
- ADR(架构决策记录) 是解药:一份轻量、追加不修改、和代码放在一起的文档,记下每个重要决策。模板就六块:标题、状态、背景、决策、考虑过的其它选项、取舍与后果。
- 强观点:记「为什么」比记「是什么」重要一百倍。 把记录的力气,花在那些一旦丢失就再也找不回来的东西上。
- 演进式架构:别想一次设计到位,而要把系统设计成「容易改对」的样子——在最可能变的地方留好清晰的接缝(模块边界/接口),让演进从「重写」变成「替换」。
- 技术债不全是坏事:它是有意识的权宜之计。纪律是「有意识地借、记下来、安排偿还时机」;危险的是无意识的、不记录的、只借不还的债。
- 康威定律:架构会长得像组织的沟通结构。拆服务前先看团队边界;也可反向用它——想要什么架构,先组织成什么团队。架构难题常是组织难题的伪装。
- 何时升级架构,靠判断不靠感觉:不是「旧了/丑了/别人上了」就升级,而是「某个真正在乎的质量属性被真实瓶颈卡住了」才升级——用 06 的尺子量,别拍脑袋。
📌 真实案例:ADR 的起源
本章讲的 ADR,来自 Michael Nygard 2011 年的博文 《Documenting Architecture Decisions》——他提出用一个个轻量小文件,记录每个决策的背景 / 决定 / 状态 / 后果。这套格式后来成了业界标准。
- 📎 社区资源与各种模板 / 工具:adr.github.io
- 本仓库每个模板的「关键决策与权衡」一节,本质就是一组写好的 ADR。
结语:成为一个「持续做出好判断、并把判断沉淀下来」的人
读到这里,整套教程就走完了。让我们回到最开始、回到这个仓库存在的理由(根 README):
写代码正在消失,而架构判断力,正变得前所未有地稀缺和值钱。
这八章,你学的从来不是某门语言、某个框架——它们都会过时,今年流行的明年就换。你学的是一种不会贬值的东西:
会贬值的(AI 正在让它廉价): 不会贬值的(越来越稀缺):
• 某个语法怎么写 • 拿到模糊需求,问出对的问题
• 某个框架的 API • 在取舍中做出有依据的选择
• 某段样板代码 • 看清系统会死在哪、该换什么
• 一次性的实现 • 把「为什么」沉淀成可复用的智慧
──────────────────────── ────────────────────────────
→ 交给 AI → 这,才是未来开发者的核心而走完这最后两章,你应该对「优秀的架构师」有了一个比第一章更完整的画像。他不只是「能设计出好系统的人」(那是 07),更是一个持续地做出好的架构判断、并把每一个判断和它背后的理由都沉淀下来的人(那是 08)。
因为单次的好设计会随时间锈蚀,但**「不断判断 + 不断记录」这个循环**,会让你和你的系统一起,越来越强:
┌─────────────────────────────────────────┐
│ │
▼ │
做出一个架构判断 ──▶ 把「为什么」记成 ADR │
(用 01-07 的方法) │ │
▼ │
业务长大 / 出现新瓶颈 ◀───────────────┘
(用 06 的尺子重新判断,回到顶端)
↑
判断力在每一圈里复利增长代码会变,框架会换,连「架构」这个词的时髦说法也会一茬接一茬。但在动手之前先想清楚系统该长什么样、并能讲清每个选择为什么——这种判断力,会在你整个职业生涯里持续增值。这,就是这个仓库想送给你的东西。
最后一个练习,然后就靠你自己了
把模板当 ADR 来读。 现在回到
../templates/,挑任意一个系统,直奔它的第 8 节「关键架构决策与权衡」。你会发现:那一节的每一条,其实就是一份压缩版的 ADR——它有背景(面临的选择)、有决策(取向)、有放弃的选项、有取舍(代价)。试着把 AI 对话产品的「决策 1:流式还是一次性返回」 按本章那张六块模板,亲手补成一份完整的 ADR。
做完这件事,你就同时拥有了这套教程的两样东西:像架构师一样『判断』(07),以及像架构师一样把判断『留下来』(08)。 剩下的,就是去真实世界里,一个系统接一个系统地练。
相关链接
- 上一章:07 · 从 0 到 1 设计一个系统 —— 第 ⑧ 步说出口的「为什么」,正是这一章要你记成 ADR 的内容
- 全程呼应:06 · 质量属性与取舍(判断何时升级架构的两把尺子)、04 · 十大核心架构模式(微服务拆分与康威定律)
- 回到起点:根 README 与 教程总览 —— 重读「这个仓库为什么存在」,你会有新的体会
- 把模板当 ADR 读:
../templates/全部模板的第 8 节(关键决策)与第 12 节(演进路线),是 ADR 思想和演进式架构最浓缩的实例