
🎯 学用 AI 辅助编写和维护架构决策记录(ADR):不是写没人读的文档,而是用 gstack 自动捕获决策上下文、替代方案、利弊权衡,让 6 个月后的你也理解为什么当初做了这个选择。 ⏱️ 预计时间:35 分钟
[!tip] 💡 白话小课堂 代码告诉你"怎么做",但不告诉你"为什么这么做"。6个月后看到一段奇怪的代码——直接改了会不会踩坑?不改又看不懂。ADR就是给每个重要技术决策留一份"当时为什么选这个方案"的备忘录。不是记录"我们做了什么"(代码已经记录了),而是记录"我们为什么这样做,为什么不选另外几个方案"(代码永远不会记录这些)。
[!warning] ⚠️ 避坑指南 别给所有决策写ADR——只给"不可逆的"决策写。可逆的决策(函数命名、文件组织)不值得花时间记录;不可逆的决策(选Kafka还是RabbitMQ、数据库怎么分片、微服务怎么拆)必须写。另一个坑:废弃的ADR不能删!它们记录了系统的演化历史——为什么当初那样做,后来为什么改了——这对新成员理解系统至关重要。
[!info] 🔑 核心秘诀 ADR最重要的部分不是"Decision"——是"Alternatives Considered"(考虑过但没选的方案及拒绝原因)。这是防止团队重复讨论的利器——6个月后新成员说"为什么不用方案B?",你指向ADR:"当时考虑过方案B,拒绝原因是X和Y。现在如果X和Y已经解决,可以重新评估。"没有这个记录,每次讨论都从零开始。
[!quote]- 📖 课程来源 本课为系统设计大师课,内容综合自真实电商工程实践经验,而非单一 Skill 文件。涵盖架构设计、分布式系统、团队管理等高级工程主题。
1. 为什么架构决策需要被记录?
代码告诉你"怎么做",不告诉你"为什么这么做"
没有 ADR 的典型场景
新人:为什么我们的订单表没有用分库分表? Tech Lead:...(回忆)当时讨论过,好像是因为...嗯... (翻 Git 历史、Slack 聊天记录、会议纪要——都找不到) 新人:那我们现在加? TL:别!我记得当时有个很重要的原因...但想不起来了 😅
ADR 解决的核心问题
不是记录"我们做了什么"(代码已经记录了) 而是记录"我们为什么这样做"(代码永远不会记录)
一条 ADR 包含: → 标题:决策的简短摘要 → 状态:提议/已接受/已废弃/已取代 → 上下文:当时面临的问题和约束 → 决策:我们选择做什么 → 后果:这个决策带来的正面和负面影响 → 替代方案:我们考虑过但没选的方案(以及为什么不选)
2. ADR 的格式与生命周期
ADR 模板
ADR-001: 使用 Redis 作为秒杀库存的扣减层
Status
Accepted (2025-11-01)
Context
双11秒杀场景:10 万 QPS,100 件商品。 传统 MySQL 行锁在 1000 并发时就出现性能断崖(P99 从 50ms → 5s)。 需要一种支持原子扣减、高并发、低延迟的库存方案。
Decision
使用 Redis DECR 命令作为秒杀库存的扣减层: → 秒杀开始前预热库存到 Redis → 扣减使用 DECR(原子操作,O(1)) → 扣减成功的订单异步写入 Kafka → 持久化到 MySQL → 15 分钟未支付自动释放库存(INCR)
Consequences
正面: → P99 延迟从 5s 降到 2ms → 支持 10 万并发无锁 → 0 超卖(DECR 原子性 + 回滚机制) 负面: → 引入 Redis 依赖(如果 Redis 挂了,秒杀不可用) → 最终一致性(Redis → Kafka → MySQL 存在短暂延迟) → 运维复杂度增加(需要维护 Redis 集群 + 主从同步)
Alternatives Considered
- MySQL SELECT ... FOR UPDATE + 库存表行锁 → 拒绝原因:并发性能差,P99 > 5s
- 库存全在 Redis,不持久化 MySQL → 拒绝原因:数据不可恢复,Redis 崩溃 = 数据丢失
- 使用 ZooKeeper 分布式锁 → 拒绝原因:性能不如 Redis DECR,运维更复杂
ADR 生命周期
Proposed(提议)→ Accepted(已接受)→ Deprecated(已废弃) → Superseded(被取代 by ADR-XXX)
不要删除废弃的 ADR!它们记录了"为什么当时那样做"—— 这对理解系统演化至关重要。
3. AI 辅助 ADR 的价值
gstack 如何提升 ADR 质量
传统 ADR 的问题
→ 写了但没审查:一个人的偏见变成了团队的决策记录 → 替代方案想不全:只列了 1-2 个显而易见的替代方案 → 后果分析不全面:只写了正面,回避了风险和代价 → 更新不及时:架构已经变了但 ADR 还显示 "Accepted"
gstack 的多角色 ADR 审查
/plan-eng-review: → 检查方案的技术可行性 → 提出遗漏的替代方案(从 2 个变成 5 个) → 评估每个方案的 pros/cons
/plan-ceo-review: → 检查方案与业务目标的 alignment → "这个技术方案是服务于什么业务目标?" → "有没有更简单且同样满足业务需求的方案?"
/codex(多模型对抗审查): → Claude 和 GPT 独立评估决策 → 如果两个模型都指出同一个风险 → 高信号 → 如果两个模型给出相反的评估 → 需要进一步分析
4. 电商案例:关键架构决策的ADR文档体系
🛒 实战案例:某电商平台3年历史,核心技术团队从3人扩展至18人后又缩减至8人(两位创始工程师离职)。新CTO上任后发现令人不安的事实:没人能完整解释"为什么订单表用Snowflake而不是UUID""为什么选了Kafka而不是RabbitMQ""为什么支付模块在独立DB中而不在订单DB中"。团队在"要不要分库分表"这个问题上已经争论了4次——每次争论都从零开始,耗费大量时间但从未形成结论。更糟的是,新成员入职平均需要3周才能开始独立贡献代码。
用 gstack 架构体系建立ADR文档体系:
- /office-hours 界定ADR范围 → gstack问CTO:"你们过去12个月做了哪些不可逆的技术决策?"→ CTO列出15个 → gstack建议:"先写最重要的12条——只记录不可逆的决策(一旦做出后改变成本极高),可逆的小决策(函数命名、布局方案)不值得写"
- /plan-eng-review 协同生成12条关键ADR:(1)事件驱动选型Kafka vs RabbitMQ vs AWS SQS、(2)微服务通信协议REST vs gRPC、(3)订单ID生成Snowflake vs UUID v7、(4)库存Redis+MySQL双层架构、(5)支付幂等性实现方案、(6)搜索技术栈ES+FAISS vs Meilisearch、(7)分库分表触发条件、(8)API版本管理策略、(9)前端状态管理、(10)CDN策略、(11)日志监控技术栈、(12)部署策略Canary vs Blue-Green
- /review 审查12条ADR质量 → 发现4个共性问题:(1)ADR-003订单ID只写Snowflake方案但未说明worker_id在K8s下Pod重启后如何分配→补充:基于Pod name hash的自动分配策略;(2)ADR-006搜索选型只比较ES和FAISS遗漏Meilisearch→补充评估(结论不变但决策更全面);(3)ADR-010 CDN策略缺少成本分析→补充:月度预估¥2400(日均300GB流量×¥8/100GB);(4)ADR-012部署策略只说选Canary没说明为什么不用Blue-Green→补充:Blue-Green需2倍资源(月增¥18000云成本),当前阶段不经济
- /codex 多模型对抗审查 ADR-004(库存架构)→ Claude和GPT独立评审→两个模型都指出"Redis崩溃后的库存重建策略描述不完整"→高信号→补充完整的灾备恢复流程
效果:
新成员入职理解核心架构:从3周口述过渡→3小时阅读12条ADR→第3天开始独立贡献代码
"要不要分库"的重复讨论终结:ADR-007明确"日订单<50万时不拆库,每季度评估一次触发条件"→此后再无重复争论
技术债偿还加速:之前看到历史遗留代码不敢改("不知道当初为什么这样写")→查ADR理解当时约束→约束已变化→果断重构(3个月内完成4个历史模块的现代化改造)
ADR持续更新:12个月内新建8条ADR,废弃3条(被新ADR取代),团队形成了"任何重大决策在PR合并前必须先写ADR"的文化
🔑 启示:ADR最重要的部分不是"Decision"(决策是什么),而是"Alternatives Considered"(考虑过但拒绝的方案及拒绝原因)。这一节防止团队在6个月后重新讨论已经被充分讨论过的方案——新成员说"为什么不用RabbitMQ?"→查ADR-001→看到"当时考虑过,拒绝原因是需要额外的运维复杂度且团队已有Kafka运维经验。如果现在运维条件已改善,可重新评估。"——讨论从零开始变成有基础的重新评估。废弃的ADR绝对不能删除——它们是系统演化的化石记录,解释了"为什么系统从A变成B"。
5. 动手实践
- 写你的第一条 ADR:选择一个你最近做的技术决策(如"为什么选择 Redis 而不是 Memcached"),用 ADR 模板(标题、状态、背景、决策、替代方案及拒绝原因、后果)写一条完整的 ADR。
- 审查一个"无文档"的决策:找一个你的项目中没有任何文档记录的重要决策,尝试逆向写出它的 ADR。然后问当时的决策者:你写的理由和他当时想的理由一致吗?
- 建立 ADR 索引:创建你项目的 ADR 目录(如
docs/adr/),写前 3 条 ADR(001-选择主数据库、002-选择 API 协议、003-选择部署方式),建立编号和模板规范。
[!tip] 提示:ADRs 的质量不在于写得多么详细,而在于"替代方案及拒绝原因"是否诚实。这一节最能体现决策者的权衡。写得模糊就是没想清楚。
6. 掌握检验
Q1:ADR 和传统技术文档(如 Wiki、README、设计文档)的核心区别是什么?
- A) 没有区别,只是换了个名字
- B) 传统文档记录"我们做了什么"(架构图、API 文档、部署方案),ADR 记录"我们为什么这样做,以及为什么不选其他方案"。代码已经记录了 What 和 How,ADR 记录的是代码永远不会记录的 Why——特别是"当时考虑过但拒绝了的方案及原因"
- C) ADR 是给开发者看的,传统文档是给 PM 看的
- D) 传统文档更好,因为更详细
Q2:一条完整的 ADR 包含 6 个部分(标题、状态、上下文、决策、后果、替代方案)。请解释为什么"替代方案及拒绝原因(Alternatives Considered)"被认为是 ADR 中最关键的部分?它是如何防止团队"重复讨论同一个问题"的?
Q3:以下哪些决策需要写 ADR,哪些不需要?请分类并说明判断标准:
- (a) 选择 Kafka 还是 RabbitMQ 作为消息队列
- (b) 给订单模块的函数 processOrder 重命名为 handleOrder
- (c) 数据库分库分表的分片键选择
- (d) 用 flexbox 还是 grid 做一个页面布局
- (e) 选择微信支付还是支付宝作为主要支付网关
- A) 只有 (a) 和 (c) 需要
- B) (a)、(c)、(e) 需要——这些都是"不可逆的决策",一旦做出后改变成本极高(数据迁移、支付集成重做);(b) 和 (d) 不需要——这些是"可逆的决策",改名或改布局可以随时重构
- C) 全部都需要写 ADR
- D) 只有 (e) 需要,因为涉及支付
Q4:gstack 的 /plan-eng-review 和 /codex(多模型对抗审查)在 ADR 质量保障中各发挥什么不同作用?如果只用 /plan-eng-review 不用 /codex,ADR 质量可能有什么缺陷?
Q5:gstack /review 审查 12 条 ADR 发现了 4 个共性问题(缺少水平扩展考量、替代方案评估不充分、缺少成本分析、未说明为什么不用其他方案)。请选择其中 2 个问题,说明:不修复这些问题,3 个月后团队可能做出什么错误的决策?
Q6:ADR 生命周期中,为什么"废弃的 ADR"(Deprecated)不能删除,必须保留在仓库中?一个被 ADR-015(Superseded by ADR-015)取代的 ADR-008,对新加入团队的成员有什么独特的价值?
7. 答案
点击查看答案
Q1:B — 代码/架构图/API 文档告诉你 How(系统怎么运作)和 What(用了什么技术),但它们永远不会告诉你 Why——"为什么选 Kafka 而不是 RabbitMQ""为什么当时没有分库""为什么订单 ID 用了 Snowflake 而不是 UUID"。这些 Why 在决策做出时在决策者的脑子里,6 个月后要么忘了要么离职了。ADR 的独特价值:把"当时为什么做这个选择,以及为什么不选其他方案"永久记录下来。特别是 Alternatives Considered——它记录了被拒绝的方案和拒绝原因,防止团队在 6 个月后重新讨论已经被讨论过的方案。
Q2:Alternatives Considered 最关键的原因:(1) 防止重复讨论——6 个月后新成员说"为什么不用方案 B?"→查阅 ADR→看到"当时考虑过方案 B,拒绝原因是 X 和 Y。如果现在 X 和 Y 已经解决,可以重新评估"→讨论从零开始变成有基础的重新评估;(2) 体现决策者的权衡质量——只考虑了一个方案=没想清楚,考虑了 3-5 个方案并写出每个的拒绝原因=真正的工程权衡;(3) 给未来系统演进提供线索——当初拒绝的理由(如成本、运维复杂度)是彼时约束,当约束变化时可以重新打开决策。
Q3:B — 判断标准:可逆性(Reversibility)。(a) 消息队列选型:不可逆——换消息队列需要迁移数据、改所有消费者生产者代码、重测消息可靠性——必须写 ADR。(b) 函数更名:可逆——IDE 重构可以随时做,零业务风险——不需要 ADR。(c) 分片键选择:不可逆——一旦数据按 sharding key 分布到多个数据库实例,改分片键需要重新分片所有数据(TB 级数据迁移)——必须写 ADR。(d) 布局方案:可逆——改 CSS 不影响业务逻辑——不需要 ADR。(e) 支付网关选型:不可逆——更换支付网关需要迁移所有签约用户的支付方式、重新集成、重做对账系统——必须写 ADR。核心原则:如果做错了可以在一周内无损改回来→不需要 ADR;如果做错了要数月时间和巨大成本才能改→必须 ADR。
Q4:/plan-eng-review 从技术可行性角度评审 ADR:检查方案是否技术上可行、是否有遗漏的替代方案(从 2 个变成 5 个)、评估每个方案的 pros/cons、检查架构一致性。这是"技术视角"的审查。/codex(多模型对抗)从不同 AI 模型的独立判断角度评审:Claude 和 GPT 独立评估同一个决策——如果两个模型都指出同一个风险→高信号(很可能真实存在);如果两个模型给出相反的评估→需要人进一步分析。/codex 的核心价值:减少单一审查者的偏见盲区。只用 /plan-eng-review 不用 /codex 的风险:审查者可能陷入群体思维或遗漏特定类型的风险——不同模型有不同的训练数据和推理倾向,交叉验证可以捕获单一模型看不到的问题。
Q5:选择(2) 替代方案评估不充分和(3) 缺少成本分析。不修复(2):ADR-006 搜索选型只比较了 ES 和 FAISS,遗漏了 Meilisearch——3 个月后团队规模从 5 人变成 15 人,某工程师发现 ES 运维太复杂,提议切换到 Meilisearch。由于 ADR 中没有 Meilisearch 的评估记录,这个讨论又从头开始:评估→对比→决策——浪费大量时间。如果当时写了 Meilisearch 评估(结论:虽然运维更简单但 ES 生态更完善,选择 ES),新成员看到直接理解。不修复(3):ADR-010 CDN 策略缺少成本分析——3 个月后 CDN 月度账单从预计的 ¥800 变成实际 ¥3200,CFO 质疑"为什么花这么多?"CTO 无法回答因为 ADR 中没有成本预估。如果有成本分析,CFO 在决策时就已认可预算。
Q6:废弃的 ADR 不能删除的两个核心理由:(1) 记录了架构演化的历史——系统为什么从 A 演化到 B。ADR-008 记录了"当时为什么选了方案 A",ADR-015 记录了"后来为什么换成了方案 B"——合在一起构成完整的架构演变故事线。如果删除了 ADR-008,只剩 ADR-015 说"我们用方案 B,取代了方案 A",但方案 A 是什么?为什么当时选 A?为什么后来放弃?这些信息全部丢失。(2) 帮助新成员理解"看似不合理的历史遗留"——新成员看到一段奇怪的代码,查 commit 不知道原因,但查相关的废弃 ADR 看到"当时因为 X 约束必须这么做,现在约束已解除"——理解了历史约束才能正确判断"现在是否该重构"。废弃 ADR 是系统的"化石记录"——删除它等于删除系统演化史的物证。
(5/6 通过)
延伸阅读
- [[L7-08-可观测性与故障响应|上一课:可观测性与故障响应 —— 系统出问题3分钟内定位而不是3小时]]
- [[L7-10-技术债金融化度量|下一课:技术债金融化度量 —— 用利率和本金让老板秒懂"代码烂"的真实代价]]
- [[L6-03-工程架构评审|相关:工程评审——ADR 的技术评估]]

评论区
共 0 条评论