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