架构决策记录Architecture Decision RecordADR是一种把架构决策及其背景写成短文档的方法。uber/ADR是 Uber 在 GitHub 上公开的一套 ADR 模板与配套规范它把一次技术选型从会议口头结论变成可检索、可追溯、可反思的工程资产。团队在维护中大型系统时代码只能说明“当前是什么”无法说明“当初为什么这样做”。ADR 要补上的正是“为什么”这一段缺失的上下文。这篇文章面向正在搭建新项目、维护老系统或者想改善团队架构评审流程的工程师。读完这篇文章你能理解 ADR 的核心概念并能以uber/ADR的模板思路为基础在自己项目里建立一套最小可用的 ADR 管理流程包括目录组织、字段设计、状态流转、评审绑定以及常见的落地失败场景和排查方式。1. 先理解 ADR 是记录架构决策的最小单元1.1 架构决策为什么需要落成文字实际项目里一次技术选型往往发生在会议、IM 群、PR 评论甚至口头讨论中。讨论结束后结论可能进了会议纪要也可能只存在于某个负责人的记忆里。三个月后新同学接手时看到代码库里用了某个中间件问“为什么选这个”答案常常是“当时评估过好像还行”。至于评估过哪些方案、放弃了什么、在什么约束下做的判断很难再拼凑完整。代码本身只表达实现结果不表达决策过程。两个服务之间选择了同步调用而不是消息队列从代码里能看到 HTTP 调用却看不到“当时吞吐量预期不高团队不希望引入额外运维组件”这些约束。如果这些背景没有被记录下来后续的重构、换型、成本优化就没有判断依据。ADR 就是用来解决这个问题的它把一次决策的背景、结论、后果写成一段结构化文字放在版本控制中和代码一起演进。1.2 ADR 文件是什么样子的一个 ADR 通常是一个短文档几百字到一千多字核心结构非常稳定。业界常见的结构来自 Michael Nygard 的提议先写 Context背景再写 Decision决策最后写 Consequences后果。uber/ADR的模板思路在这个基础上做了工程化加工加入元数据区域、状态字段、关联字段让文档可以被检索、被校验、被追踪。下面是一个最简形式的 ADR 内容示意只说明结构不包含完整细节# ADR-0001: 使用 Redis 作为缓存层 ## Status Accepted ## Context 当前服务存在大量重复查询数据库压力大。 ## Decision 引入 Redis 作为缓存层缓存热点数据。 ## Consequences 更快的读取速度但增加了一个基础组件需要运维。这种文件不需要很长。它足够告诉未来的人当时的背景是什么团队做了什么选择代价是什么。更工程化的模板会在这个基础上增加 Front Matter这是后续要展开的部分。1.3 ADR 与常规设计文档的区别团队里通常已经有架构设计文档、技术方案评审材料等容易产生疑问是不是又多了一种文档这里要明确ADR 的定位和设计文档不同。维度架构设计文档ADR 架构决策记录目标描述系统整体方案、模块关系、流程记录一次决策的背景、选择和后果篇幅通常较长几十页也常见短小轻量一般控制在几百字到一千多字更新方式随设计演进持续修改决策确定后保持稳定后续变化用新 ADR 替代读者评审者、开发团队、新成员未来的维护者、架构评审者、需要复盘的人维护成本高需要持续同步低写一次状态变化时更新元数据典型问题设计文档和代码经常脱节如果不写决策背景会永久丢失关键差异在于设计文档回答“系统是怎么设计的”ADR 回答“这个决策是怎么来的为什么这样做”。两者的生命周期不一样设计文档会随着方案迭代持续修改ADR 则在决策成立后尽量保持不被篡改确有必要的变化通过新 ADR 来体现。2. 理解 uber/ADR 的模板结构与文件组织2.1 文件命名与存储位置落地 ADR 的第一步不是写内容而是先把文件组织定清楚。推荐在仓库根目录下建立docs/adr目录把决策记录和普通文档分开。名称上使用“数字前缀 短横线语义化标题”的格式例如docs/adr/0001-config-center.md docs/adr/0002-cache-redis.md docs/adr/0003-message-queue-kafka.md数字前缀用于排序也用于生成 ADR 的唯一标识。如果使用日期作为前缀同一天出现两条决策会比较麻烦使用递增序号更稳定。slug部分要尽量简洁能让人从文件名判断这次决策主题。在 GitHub 风格仓库中路径可以直接链接到对应 PR评审时方便引用。目录结构示例project/ ├── docs/ │ └── adr/ │ ├── README.md │ ├── template.md │ ├── 0001-config-center.md │ └── 0002-cache-redis.md └── src/README 负责说明本目录的规则状态有哪些、模板在哪个文件、谁可以修改状态。模板文件则作为新决策的起点。目录结构一旦确定就不要频繁调整否则已有的链接和引用都会失去作用。2.2 元数据Front Matter字段工程化的 ADR 通常会在 Markdown 文件顶部加入 YAML Front Matter用统一字段存放结构化信息。uber/ADR项目的核心价值之一就是把这套字段和模板暴露给团队使不同团队写出的 ADR 保持同一风格。一个常见的 Front Matter 示例--- id: ADR-0001 title: 引入配置中心管理多环境配置 status: Accepted date: 2025-01-15 decision-makers: - name: 张三 role: 后端负责人 - name: 李四 role: 架构师 considered-options: - name: 自研配置系统 pros: 完全可控 cons: 开发维护成本高 - name: 开源配置中心 pros: 功能成熟社区活跃 cons: 需要引入额外依赖 chosen-option: 开源配置中心 related-adrs: - ADR-0005 ---字段不是越多越好但下面几个建议保留字段含义示例必要性id决策唯一标识用于链接和讨论ADR-0001必填title决策标题一句话概括引入配置中心管理多环境配置必填status当前状态Accepted必填date决策创建或接受日期2025-01-15必填decision-makers参与决策的人列表形式建议considered-options被考虑的候选方案列表形式建议chosen-option最终选择的方案开源配置中心必填related-adrs关联的 ADR 编号[ADR-0005]按需为什么要写决策人因为后续有人对决策有疑问时最先要找的就是当时的决策人。为什么要列候选方案因为被否决的方案往往比被选中的方案更有信息量它记录了团队评估范围的边界。使用 YAML 而不是纯文本段落是因为字段可以被检索也能在 CI 脚本中做校验这是结构化带来的直接好处。2.3 正文结构Front Matter 下方是正文正文部分建议按固定的标题顺序展开。下面是一个可以在template.md里使用的结构## Context描述背景、问题、约束条件和触发原因。## Decision写出最终决策使用准确、可执行的表述。## Consequences列出决策带来的收益、成本、风险和后续影响。## Alternatives Considered列出候选方案和否决原因。## References指向相关代码、Issue、PR 或外部文档。以“缓存层选型”为例一个完整的 ADR 可以写成--- id: ADR-0002 title: 引入 Redis 作为缓存层 status: Accepted date: 2025-02-10 decision-makers: - name: 张三 role: 后端负责人 considered-options: - name: 本地内存缓存 pros: 零依赖实现简单 cons: 多实例不共享缓存一致性问题 - name: Redis pros: 读写性能好支持过期和持久化 cons: 需要维护 redis 服务 chosen-option: Redis related-adrs: - ADR-0001 --- ## Context 订单查询接口每天产生大量重复查询数据库主库负载接近 70%。经过压测 热点商品详情查询的 QPS 达到 3000其中约 80% 请求访问的是同一批热点数据。 ## Decision 引入 Redis 作为缓存层对商品详情、用户会话等热点数据做缓存。 缓存 key 统一使用 order:detail:{orderId} 格式数据更新时同步失效。 Redis 以集群模式部署先部署 3 节点后续根据监控扩容。 ## Consequences - 数据库读压力明显下降QPS 峰值时可支撑当前业务的 3 倍。 - 需要新增 Redis 运维能力包括监控、告警、备份和故障演练。 - 缓存淘汰、序列化、key 规范需要一并纳入团队约定。 ## Alternatives Considered - 本地内存缓存实现简单但多实例部署时无法共享最终放弃。 - Memcached适合纯 key-value但缺少持久化能力放弃。 ## References - 性能压测报告见 issue #188 - 部署方案见基础设施仓库 infra/redis这段示例里的每个部分都有具体含义。Context 里写的是“为什么现在要做”Decision 里写的是“具体选择了什么以及怎么用”Consequences 里同时写了收益和成本Alternatives 给出了被否决的方案。这样一份 ADR 已经具备做后续决策依据的价值。3. 在自己项目里落地一套最小可用的 ADR 流程3.1 先搭目录和规范落地不要追求一步到位。先用最简单的方式跑通流程再逐步增加约束。第一步是建立目录和模板。mkdir -p docs/adr touch docs/adr/README.md touch docs/adr/template.mdREADME 里至少写清楚三件事ADR 的存放位置和文件命名规则。状态有哪些分别代表什么。新增决策的流程创建文件、填写模板、提交 PR 评审、合并后算生效。这些内容不一定长但必须让新成员第一次看到就知道怎么用。模板文件可以直接参考上一节的正文结构保留字段占位符。检查点确认目录结构存在README 有明确规则模板文件可以复制使用。这里要特别注意模板文件里的id不要写成固定值