假设你是一支探险队的领队。队伍走到峡谷分叉口:左边近但雨季可能塌方,右边远但驮马好走。你思考后选了左边,三天后果然遇到落石。如果当时只是随口说“走左边”,新加入的队友会反复追问为什么;如果写进旅行手账,记下当时的天气、地势、权衡和风险,大家一翻就懂。ADR 就是架构团队的“旅行手账”:每一个重要技术决策,都用一页轻量文档记下当时发生了什么、为什么选、选完会怎样。它不写沿途风景,只记关键路口。 当 AI 工程师面临模型选型、特征平台或推理引擎决策时,ADR 能把“当时为什么这么选”变成团队资产,避免同类问题每周重吵一遍,也让评审、交接和复盘有据可依。
对 AI 从业者而言,ADR 是方案设计文档中最轻却最值钱的一页:它让技术决策从“口头记忆”升级为“团队共识”,是算法、工程、产品高效对齐的底层工具。
可视化演示
动态演示架构决策记录ADR方法的核心机制,通过基础/核心/应用/扩展四种模式展示关键概念与交互流程。
本节配有交互动效(进入视口后自动加载,约 3KB)。
架构决策记录ADR方法:架构决策 架构决策记录ADR方法论 决策上下文的捕捉方法 核心机制可视化 架构决策记录ADR方法整体架构与关键组件
什么是 ADR
探险队的旅行手账里,不会把每一块石头、每一棵草都记下来。它只记录岔路口:你面前有哪几条路、天气如何、体力还剩多少、最后选了哪条。ADR 就是技术世界里的“岔路口手账页”,全称 Architecture Decision Record,翻译过来是“架构决策记录”。它用一页轻量 Markdown 文档,把一次重要技术选型的来龙去脉固定下来。
很多人以为 ADR 是“写文档”,其实它更像“拍板时拍张照”。当团队决定“用 PostgreSQL 还是 MongoDB”“AI 推理用 TensorRT 还是 ONNX Runtime”,这些选择往往影响后续几个月甚至几年。ADO 不记录每一行代码怎么写,只记录“当时为什么做这个决定”,让后人不必把所有讨论重新经历一遍。
一条 ADR 通常由四个要素组成:背景、决策、状态、后果。背景是当时的约束和可选方案;决策是最终选择;状态表示这条记录目前是提议、已接受还是已废弃;后果是选择带来的收益和风险。这个结构保证了任何一个人拿起记录,都能在几分钟内复原当时的思考现场。
团队在写 ADR 时,不必追求长篇大论。手账的价值在于“当时有感而发”,而不是“事后补齐史诗”。只要站在未来三个月后的自己面前,问一句“能不能看懂”,就足够了。
- 背景记录问题发生时的约束、触发事件和候选方案,相当于手账里的日期、天气和路况。
- 决策记录最终选择和选择理由,相当于“我最终走了左边,因为雨季前能过河”。
- 状态标记这条记录当前属于提议、已接受、已替代还是已废弃,相当于便签上贴着“草稿”“已盖章”或“已作废”。
- 后果记录选择带来的正面收益和潜在风险,提醒未来的自己“这条路可能遇到什么”。
ADR 的核心价值
探险队最怕的不是选错路,而是忘记当初为什么选。一个季度后,老队员走了,新队员问“为什么系统用了这个奇怪的队列”,所有人面面相觑。如果手账上写着“当时评估过 RabbitMQ,但团队没有运维 Erlang 的经验,所以选了 Kafka”,这个问题就不会再被翻来覆去地讨论。
ADR 的核心价值是“决策可追溯”。它把散落在聊天记录、会议纪要和大脑记忆中的信息,压缩成结构化的文字。任何时间点,团队都能通过一条 ADR 回答三个问题:当时要解决什么?为什么选这个方案?我们放弃了什么?这层可追溯性,是 AI 团队快速迭代时最容易忽略却最值钱的基础设施。
从成本角度看,写 ADR 的边际成本很低,但重复讨论的边际成本很高。一次五人的技术评审会可能消耗两小时,而一条 ADR 只需要十分钟阅读。ADR 越多,旧问题越不容易被反复翻出来,团队也越能把精力放在新问题上。
在 AI 项目中,模型选型、特征口径、推理部署等决策经常横跨算法、工程、产品多个角色。ADR 让这些角色不用每次都回到原始出发点争论,而是先看手账,再决定是否真的需要变。
- 重复讨论指团队反复论证同一技术选择所消耗的时间,相当于探险队一次次回到同一个岔路口重新探路。
- 反比关系ADR 数量增加后,讨论次数会下降,因为答案已经写进手账。
- 加一处理避免“还没有任何 ADR”时分母为零,同时表达“哪怕只有一条记录,也比零条好”。
ADR 模板结构
旅行手账有一种固定版式,你绝不会把封面和正文混在一起。ADR 也有一套约定俗成的模板,让团队扫一眼就知道该去哪里找信息。下面是常见的最小模板:标题、状态、背景、决策、后果。
- 标题:一句话说清“做了什么决策”,例如“采用向量数据库作为知识库检索后端”。
- 状态:当前是提议、已接受、已替代还是已废弃。
- 背景:解释为什么要做这个决策,包含触发器、约束和候选方案。
- 决策:写出最终选择以及为什么选它。
- 后果:写明正面收益、负面影响,以及未来需要重新评估的信号。
一个常见的写法是“背景 + 决策 + 后果”三段式。背景回答“为什么会走到这个路口”,决策回答“走了哪条路”,后果回答“这条路会带来什么”。状态字段放在标题下,像便签上的红章,提醒读者这条 ADR 是否还有效。
模板不是枷锁,而是“最低共识”。如果团队觉得三段不够,可以增加“备选方案”或“参考链接”;如果觉得太啰嗦,也可以压缩成几句话。关键是所有人都遵循同一套结构,这样跨项目检索时才不会迷路。
- 标题和状态作为记录的开头,让人一眼知道“这是什么决策、当前是否有效”,相当于手账页上的目录和印章。
- 背景记录触发事件和环境约束,相当于“为什么这个时间点要决定路线”。
- 决策记录最终选择和关键理由,是整页手账的主体。
- 后果记录收益、成本和风险,帮助后人判断这条 ADR 是否需要被重新审视。
ADR 生命周期
手账里的每一页并不是永远有效。有的便签写着“初步计划”,后来被划掉;有的页面被新的一页取代,旧页被贴上一张“已废弃”的标签。ADR 的生命周期也是如此:它从“提议”开始,经过评审成为“已接受”,之后可能因为新决策而进入“已替代”或“已废弃”。
生命周期中的四种常见状态是:提议(Proposed)、已接受(Accepted)、已替代(Superseded)、已废弃(Deprecated)。提议表示还在讨论;已接受表示团队拍板确定为当前决策;已替代表示已经被新 ADR 取代;已废弃表示该决策不再适用但保留存档。
状态变化最重要的原则是“旧记录不能静默消失”。当一条新 ADR 被接受并取代旧方案时,必须回到旧 ADR 中把状态改成“已替代”,并链接到新 ADR。这就像手账里贴一张交叉引用:“想看最新路线?翻到第 18 页。”只有这样的回链,决策谱系才完整。
在 AI 领域,模型、框架和基础设施迭代极快。一条“选用 BERT”的 ADR 可能在半年后被“改用大模型 API”的 ADR 替代。这不是失败,而是团队认知升级的证据。
- 提议记录还在草稿阶段,尚未获得团队认可,相当于便签上写着“待定路线”。
- 已接受经过评审后成为团队默认遵循的决策,相当于手账被盖上“正式路线”的章。
- 已替代当新 ADR 出现并取代旧决策时,旧 ADR 状态改为已替代,形成“新旧决策链”。
- 已废弃不再适用且没有直接的继任者,但仍作为历史存档保留。
ADR 落地流程
有了一本漂亮的手账,并不等于队伍会用。落地流程要回答的关键问题是“什么时候写、谁来审、谁来维护”。如果你要等周五下班前统一补记录,那手账大概率会变成流水账;如果每次选路时顺手写一页,它才能保持鲜活。
落地流程可以分成五步:触发、起草、评审、合并、维护。触发条件通常是一次有长期影响、难以无损回退的技术选型;起草要求负责人在做决定前或决策当天写下 ADR;评审可以放在代码评审(PR)中异步进行,不必额外开会;合并意味着把 ADR 文件随代码一起提交到 Git 仓库;维护则是在后续变化时更新状态或补充后果。
团队可以约定一个极简阈值:如果一个决策需要开会讨论超过十五分钟,就值得写一条 ADR。这句话能过滤掉大量琐碎选择,让团队把力气花在真正影响长期架构的问题上。
一次完整的 ADR 落地不需要超过半小时。正是这种轻量,让它可以持续运转,而不是像一套重型文档流程那样虎头蛇尾。
- 触发识别“这是重要岔路口”,而不是沿着老路继续走。
- 起草把背景、方案和想法写成草稿,相当于边看路况边写手账。
- 评审通过 PR 或评审会收集反馈,相当于让有经验的队员看一遍手账。
- 合并ADR 文件进入代码仓库,成为长期维护的知识资产。
- 维护当新 ADR 出现或旧决策不再成立时,更新旧 ADR 的状态。
常见反模式
再好的旅行手账,如果变成流水账,也会失去意义。ADR 实践中最常见的失败,不是“没人写”,而是“写成了没人看的东西”。比如有人把每个候选方案的所有实验数据、每段聊天记录都塞进 ADR,结果是真正想找答案的人根本找不到重点。
另一个典型问题是没有背景。只写“选择方案 A”,不写“为什么不用方案 B”,后人拿到手账也只会记住结论,无法重建当时的权衡。还有团队在 ADR Accepted 之后再也没改过状态,旧决策已经实际失效,页面还挂着“已接受”,误导后来的人。
一条 ADR 的健康度,可以用“仍然被阅读、仍然被引用”来衡量。如果大量 ADR 躺在仓库里吃灰,说明它已经变成文档负债。健康的 ADR 库应当像一本常翻常新的手账:该废弃的及时标记,该引用的随手可达,不该写的绝不硬凑。
所以,写 ADR 之前要问自己一句:这一页,未来会有人翻吗?如果答案是“不会”,那就别写;如果答案是“会”,就把它写清楚。
- 仍被查阅指 ADR 在评审、开发或复盘时被实际引用,相当于手账被新队员翻旧的那几页。
- 全部 ADR包含活跃记录、已替代记录和已废弃记录的总数。
- 比值含义健康度越高,ADR 库越接近“知识地图”,而不是“文档垃圾场”。
工具与协作
手账记得再多,如果没有编号和目录,也只是一堆散纸。ADR 的协作机制同样需要一套“收纳系统”:统一编号、统一命名、统一存放。最常见的做法是在仓库根目录创建 docs/adr/ 文件夹,每个文件命名为 NNNN-标题.md,例如 0001-使用向量数据库.md。
编号可以从 0001 开始连续递增,也可以按年份分段,例如 2025-001。命名规范要求标题中的单词用短横线连接,方便在命令行和 Git 里查看。这类规范不需要复杂工具,纯 Git 就能管理:ADR 随代码一起提交,改动通过 PR 评审,历史通过 Git 回溯。
如果团队希望更高效,可以使用 adr-tools、adr-log 等周边命令行工具,自动生成目录、建立状态索引,甚至渲染成静态站点。但更重要的是团队共识:新决策先查已有 ADR,评审 PR 时把 ADR 当作一等公民,而不是可选的“附加文档”。
工具只是脚手架,真正让 ADR 活下去的是团队的共同习惯。每次重要选型,大家养成条件反射式地打开 docs/adr/ 翻一翻、写一写,这本旅行手账才会成为团队真正的集体记忆。
- 编号 NNNN从 0001 开始的递增数字,表示 ADR 的先后顺序,相当于手账连续页码。
- 标题用短横线连接的关键词,概括决策主题,便于搜索和扫描。
- 扩展名 .md使用 Markdown 格式,与代码一起进行版本管理,保证可读和可追溯。
AI 应用场景
场景一:AI 模型选型决策
阶段:方案设计
解决的问题:团队选择大模型 API、开源模型还是本地小模型时,经常因为成本、精度、隐私反复摇摆。传统方式下,每次方案评审都要重新解释一遍约束,ADR 可以把约束和权衡固定下来。
具体案例:某团队在客服意图识别中,起初选用 BERT 本地部署。后来业务数据量增长,leader 提出换用 GPT API。团队没有直接开吵,而是先翻到旧 ADR,看到“必须在私有化环境部署”的约束,再讨论“是否已经允许数据出域”。最终写出一条新 ADR,将 BERT 标记为“已替代”。
场景二:特征平台选型
阶段:特征工程
解决的问题:离线训练和在线推理的特征不一致,是推荐系统常见隐患。传统方式口头约定特征来源,换人后容易失传。
具体案例:数据团队决定引入特征仓库(Feature Store)统一管理特征。ADR 记录下当前特征数量、更新频率、在线延迟要求,以及为什么用 Feast 而不是自研方案。三个月后 Feast 社区方向变化,团队凭 ADR 快速评估并迁移到自研服务。
场景三:推理引擎选型
阶段:部署运维
解决的问题:AI 模型上线时,推理引擎选择直接影响延迟和吞吐。每次换人维护都要重新理解当初为什么选 TensorRT、ONNX Runtime 或 vLLM。
具体案例:部署团队在 LLM 服务化时,用 ADR 记录当时显存大小、并发预估、框架生态成熟度,并明确“如果 QPS 超过 500 需重新评估”。后来流量翻倍,团队依据 ADR 快速启动新一轮选型,不必重新调研旧问题。
Python 演示
from dataclasses import dataclass @dataclassclass ADR: seq: int title: str status: str context: str decision: str consequence: str def file_name(self) -> str: return f"{self.seq:04d}-{self.title}.md" def to_markdown(self) -> str: return (f"# {self.seq:04d}-{self.title}\n\n" f"状态:{self.status}\n\n" f"## 背景\n{self.context}\n\n" f"## 决策\n{self.decision}\n\n" f"## 后果\n{self.consequence}\n") class ADRLog: def __init__(self): self.records = [] self._seq = 0 def add(self, title, context, decision, consequence): self._seq += 1 adr = ADR(self._seq, title, "已接受", context, decision, consequence) self.records.append(adr) return adr def supersede(self, old_seq, title, context, decision, consequence): if 1 <= old_seq <= len(self.records): self.records[old_seq - 1].status = "已替代" self._seq += 1 new_adr = ADR(self._seq, title, "已接受", context, decision, consequence) self.records.append(new_adr) return new_adr def render_all(self) -> str: return "\n\n---\n\n".join(r.to_markdown() for r in self.records) if __name__ == "__main__": log = ADRLog() a1 = log.add( "选择消息队列", "服务需要异步解耦,团队无 Erlang 运维经验", "采用 Kafka 作为消息中间件", "运维复杂度上升,但生态最成熟" ) a2 = log.add( "引入特征平台", "离线在线特征不一致导致模型效果波动", "采用 Feast 统一特征口径", "需要学习新组件,但特征一致性提升" ) a3 = log.supersede( a2.seq, "替换特征平台", "Feast 运维成本高,社区方向调整", "改为自研特征服务", "保留数据迁移窗口,旧 ADR 标记为已替代" ) print(a1.file_name()) print(log.render_all())
adr = ADR(self._seq, title, "已接受", context, decision, consequence)创建一条正式 ADR,相当于向旅行手账里贴入新的一页。self.records[old_seq - 1].status = "已替代"新决策出现时,把旧 ADR 状态改为“已替代”,保证决策链条完整。def to_markdown(self) -> str:将 ADR 渲染为 Markdown,方便直接写入docs/adr/NNNN-标题.md。f"{self.seq:04d}-{self.title}.md"按 ADR 规范生成四位数编号文件名,例如0001-选择消息队列.md。log.render_all()一次性导出全部 ADR,形成团队可检索的决策手账。
常见误区
误区我们团队讨论完拍板就行,写 ADR 只会拖延进度。反正写出来也没有人看,和那种几十页的设计文档一样是摆设。
正解ADR 不是给管理层看的汇报,而是给未来队友的决策地图。讨论完只存在记忆里,三个月后人员变动,决策理由就蒸发了。写得好的 ADR 只有一两百字,却能让新人不问第二遍。
为何常见:很多人见过又长又空的文档,所以下意识把 ADR 也归入“文档负债”;其实它的价值在后续复用,而不在完成当下。
误区写 ADR 就应该把所有候选方案的实验数据、聊天记录、每人发言都贴进去,信息越全越有价值。
正解ADR 的目标是快速读、长期活。细节太多会变成维护负担,而且很快过时。真正该保留的是“为什么选”和“影响什么”,而不是“全部讨论过程”。
为何常见:写的人担心漏信息被追问,于是把文档写成流水账,反而掩盖了决策主线。
误区ADR 的状态字段只是形式,写的时候填一次“已接受”,之后就再也不用管了。
正解技术世界没有永恒答案。新约束出现后,新 ADR 可以替代旧 ADR,旧状态要改为“已替代”。这不是打脸,而是诚实的变更日志。
为何常见:把决策当成一次性终稿,忽略决策本身会随着环境、数据和团队认知的变化而失效。
误区写 ADR 是大厂和外企的重型流程,我们小团队口头默契就够,写文档反而降低效率。
正解小团队更需要轻量 ADR,因为人少、知识集中,一旦有人离开断档更严重。ADR 的最小成本只有一百字,不存在“流程重不重”的问题。
为何常见:把 ADR 误解为必须在评审会上层层审批的管理制度,没意识到它从一条 PR 就能开始。
面试题
Q1. ADR 的主要作用是什么?
- A.代替代码评审
- B.留存决策依据← 正确答案
- C.替代需求文档
- D.记录日常琐事
答案:B。ADR 的核心是“留存决策依据”,把重要技术选型的上下文、理由和结果记录下来。它不替代代码评审,也不替代需求文档,更不是日常流水账。
Q2. 哪一项是 ADR 标准结构通常包含的?
- A.代码注释和缩进
- B.背景、决策、状态、后果← 正确答案
- C.测试用例和覆盖率
- D.会议纪要和签到表
答案:B。ADR 的经典模板是背景、决策、状态、后果四要素。代码注释、测试覆盖率和会议签到都不属于 ADR 的固定结构。
Q3. ADR 状态从“已接受”变为“已替代”意味着什么?
- A.该 ADR 被管理员删除
- B.该 ADR 被新的决策记录替代← 正确答案
- C.该 ADR 自动失效且无需说明
- D.该 ADR 升级为需求规格
答案:B。“已替代”表示新 ADR 取代了旧 ADR,旧记录保留但状态更新。它不是删除,也不会自动失效,更不能升级成需求规格。
Q4. 设计 AI 推荐系统时,最值得写 ADR 的时刻是?
- A.调整单次学习率
- B.选择模型总体架构← 正确答案
- C.修改提示词标点
- D.重命名代码变量
答案:B。ADR 适合记录有长期影响、难以无损回退的选择。模型总体架构直接影响后续开发,值得记录;学习率、标点和变量改名属于可随时调整的细节。
Q5. ADR 适合记录哪类内容?
- A.有长期影响的重要技术选择← 正确答案
- B.任一行代码的编写过程
- C.团队所有口头讨论内容
- D.每日例行工作的完成情况
答案:A。ADR 的目标是捕捉高价值决策。一行代码的编写、所有口头讨论和每日工作日志都不值得写成 ADR,否则会淹没真正重要的信息。
Q6. 一份合格的 ADR 应当包含哪些内容?
- A.当时的背景和约束← 正确答案
- B.最终决策和选择理由← 正确答案
- C.可预见的正面与风险← 正确答案
- D.每个文件的具体行号
答案:ABC。背景、决策理由和后果影响是 ADR 的必备内容。具体文件行号属于实现细节,不应该出现在 ADR 中。
Q7. 关于 ADR 生命周期,下列哪些操作正确?
- A.新ADR接受后旧ADR改已替代← 正确答案
- B.过时的ADR可以标记已废弃← 正确答案
- C.ADR接受后不可再改状态
- D.PR评审可讨论ADR草稿← 正确答案
答案:ABD。ADR 是活文档,状态可以随环境变化而更新。新 ADR 接受后旧 ADR 应改为“已替代”;过时可以标记“已废弃”。ADR 草稿完全可以在 PR 评审中讨论。
Q8. 一个 ADR 编号为 0012-引入向量数据库.md,从文件名能直接得到什么信息?
- A.数据库的安装方式
- B.ADR 顺序和决策主题← 正确答案
- C.代码仓库的提交数量
- D.团队的绩效排名
答案:B。NNNN-标题.md 是常见 ADR 命名规范,表示顺序编号和决策主题。文件内容才是安装方式,文件名与提交数量、绩效无关。
Q9. 以下哪些做法有助于 ADR 实践落地?
- A.在 PR 中要求重大选型附带 ADR← 正确答案
- B.把 ADR 文件与代码一起版本管理← 正确答案
- C.新决策先查已有 ADR 再讨论← 正确答案
- D.同一个 PR 中写二十条 ADR
答案:ABC。把 ADR 接入 PR 流程、随代码版本管理、先查旧记录再决策,都是健康实践。一次塞进二十条 ADR 会让评审失去焦点,不是好主意。
Q10. 团队有人质疑旧 ADR 中的选型,最优回应是?
- A.无视质疑继续执行
- B.翻看对应 ADR 并核对权衡← 正确答案
- C.删除旧 ADR 后写新的
- D.要求对方写论文论证
答案:B。面对质疑,先打开对应 ADR,确认当时的背景和权衡,是最合理的做法。直接无视或删除都不严谨,要求写论文则矫枉过正。