ADR 不是新概念。AI Agent 进入代码库后,它的价值被重新放大:代码库必须留下当初为什么这样做。
导语
很多团队都遇到过同一种维护现场。
接手一段老代码,看到一个明显不够优雅的实现:多绕了一层判断,缓存策略看起来保守,接口字段命名也不像今天的风格。第一反应通常是重构掉它。
但问题在于,这段代码也许不是随手写坏的。它可能绕过了某个线上兼容问题,也可能牺牲了一点性能去换稳定性,还可能是为了适配一个已经没人记得的外部约束。
如果作者还在,问一句就能知道答案。作者离职、聊天记录散落、评审文档过期时,剩下的就只有猜,Agent 凭感觉直接"优化",然后把当年的坑重新踩一遍。
这背后的根因很简单:代码记录了怎么做 ,却没有记录为什么这么做 。而在维护、重构、协作和 AI 介入开发时,"为什么"往往才是最贵的上下文。
图:代码告诉 Agent 系统怎么运行,ADR 告诉它当初为什么这样设计。
ADR 记录的是技术决策,不是流水账
ADR,全称 Architecture Decision Record,通常翻译为架构决策记录。它是一种轻量文档,用来记录重要技术决策、决策背景、被放弃的备选方案,以及这些选择会带来的后果。
它最早由 Michael Nygard 在 2011 年提出。核心思想并不复杂:把架构决策当成代码资产的一部分,放进仓库,纳入版本控制,随着 PR 一起 review。
一份足够可用的 ADR 不需要复杂模板。一个典型结构可以很短:
# ADR-007: 用带 TTL 的 LRU 缓存替代 sync.Map
Status: Accepted
Date: 2026-07-14
Context:
chatModelCache 当前使用 sync.Map,没有淘汰策略。压测中内存随请求量持续增长,存在 OOM 风险。
Decision:
改用带 TTL 的 LRU 缓存,容量上限 10000,过期时间 30 分钟。
Consequences:
- 内存上限可控,降低 OOM 风险
- 缓存命中率会略有下降
- 引入新的缓存依赖,需要纳入升级维护
Supersedes:
ADR-003
真正重要的不是字段数量,而是几个约束:
| 约束 | 含义 | 工程价值 |
|---|---|---|
| 一决策一文件 | 每个重要决策单独成篇,编号递增 | 可引用、可检索、粒度清晰 |
| 和代码同仓 | 放进 git,跟随 PR 一起演进 | 决策与实现拥有同一条生命周期 |
| 状态明确 | Proposed、Accepted、Deprecated、Superseded | 一眼判断当前决策是否仍然有效 |
| 可推翻但不抹除 | 新 ADR 推翻旧 ADR,旧记录保留 | 保留系统演进轨迹,避免重复争论 |
这套机制真正保存的不是结论,而是当时为什么只能这么选。三个月后有人质疑这个设计,仓库里能给出答案;三年后系统已经改过很多轮,团队仍然能看见决策是怎么一步步走到今天的。
讨论可以在协作工具里,结论必须跟代码走
很多团队会把技术评审放在飞书、Confluence、Notion 这类在线协作工具里。这当然合理。讨论过程需要评论、@、多方协同、富文本和临时草稿,不一定适合全部塞进代码仓。
但问题出在另一处:讨论结束后的结论,如果只留在线文档里,就会和代码分家。
代码改了三版,评审文档还停在初稿。git blame 能查到谁改了这一行,却查不到当初为什么这么改。新人接手时找不到上下文,AI Agent 接手时更找不到。
更稳妥的分工是:
| 内容 | 适合位置 | 原因 |
|---|---|---|
| 讨论过程 | 在线协作工具 | 适合多人评论、临时方案、争论和非工程角色参与 |
| 决策结论 | 代码仓 ADR | 适合版本控制、diff、review、检索和长期维护 |
也就是说,讨论可以留在协作场,但结论必须跟着代码走。
甚至从长期看,讨论本身也有进入仓库的价值。只是当下最该先做的是把结论收进来,因为它成本低、收益直接,也最容易被 Agent 消费。
图:讨论可以发生在协作工具里,但被接受的技术决策应进入仓库并随代码演进。
ADR 过去难坚持,是因为收益太晚
ADR 不是新东西。它提出很多年了,很多团队也试过,但常见结局是开头认真写几篇,后面慢慢停掉。
原因不复杂:写 ADR 的成本发生在现在,收益却押注在未来。
写的人要补背景、整理取舍、描述后果。可当下代码照样能合,需求照样能上线,团队也不会因为少写一条 ADR 立刻出事故。它的读者是"未来某个可能接手的人"。这个人可能永远不会来,来了也可能直接找你问一句。
所以 ADR 过去更像一种职业自觉。自觉当然重要,但它很难对抗交付压力。
但现在 AI Agent 改变了这笔账。
AI 让 ADR 的写作成本也下降了
AI 不只改变了读者,也改变了写作者的成本。
过去写 ADR 是从空白页开始。现在可以让 AI 先基于代码、PR diff、测试和变更说明、聊天记录等起草一版"现状解释",人再补上 AI 读不出来的部分。
这个分工很清楚:
| 角色 | 更擅长记录什么 |
|---|---|
| AI | 代码当前做了什么、模块关系是什么、变更影响哪些路径 |
| 人 | 当初为什么选这个方案、否掉了什么方案、有哪些隐性约束 |
AI 可以把"是什么"写得很快,人只需要补少量"为什么"。这会把 ADR 从额外写作,变成一次决策确认。
甚至对存量代码也一样。过去给老代码补 ADR 接近考古,很少有人愿意做。现在可以让 AI 扫模块、生成现状快照,人再挑关键决策补理由。存量系统第一次有了低成本补上下文的机会。
没有 ADR,Agent 会自信地猜错
这不是文档洁癖,而是 AI 协作下的可靠性问题。
一个没有决策记录的代码库,人看不懂时还能问人、翻历史、凭经验判断。Agent 主要依赖仓库中的显式上下文。没写下来的约束,对它来说就等于不存在。
典型风险包括:
| 场景 | 没有 ADR | 有 ADR |
|---|---|---|
| 重构老代码 | Agent 把故意保守的实现当技术债优化掉 | Agent 读到历史约束,知道这段代码不能简单替换 |
| 方案反复 | 换一个人或 Agent,又提出三个月前已否掉的方案 | Supersedes 链条记录旧方案为什么被否 |
| 架构漂移 | 每次局部修改都按当前上下文自由发挥 | ADR 提供架构基线,变更要么对齐,要么显式推翻 |
| 隐性约束丢失 | 兼容性、灰度、依赖限制只存在于人脑中 | 约束变成 Agent 可检索的工程上下文 |
ADR 在这里起到的作用,是给 Agent 加一道"先别急着改"的刹车。它把人脑里的隐性知识,转成机器能读到的显性上下文 。
图:没有决策记录时,Agent 很容易把历史约束误判成可以清理的技术债。
ADR 与测试、契约、CI 不是一类护栏
在 AI 工程里,常见护栏包括测试、CI、类型检查、接口契约、lint 规则。这些工具守住的是"代码现在对不对"。
但它们回答不了另一个问题:这段代码为什么长这样?
测试能告诉 Agent 不能破坏某个行为,但不能解释这个行为当初为什么存在。接口契约能告诉它字段必须叫这个名字,但不能解释为什么选了这种边界。CI 能拦下回归,却不能拦下"看似合理、实则误解意图"的改动。
ADR 补的是意图层。
| 护栏 | 主要回答 |
|---|---|
| 测试 | 行为有没有坏 |
| 类型和契约 | 接口边界有没有破 |
| CI | 工程规则有没有过 |
| ADR | 这套设计为什么成立 |
测试和契约让 AI 不轻易改坏,ADR 让 AI 不轻易误解。
落地时要轻,不要追求完美
ADR 最常见的失败方式,不是写得不够正式,而是写了几篇就停了。要让它持续,关键是门槛必须低。
可以从五条规则开始:
- 只记录重要决策。数据模型、接口契约、模块边界、关键依赖、上线和回滚策略,值得写;普通实现细节不必写。
- 一篇 ADR 控制在十分钟内能完成。背景、决策、后果、状态、是否推翻旧决策,足够用了。
- 和代码放在同一个 PR。决策和实现一起 review,一起合入。
- 旧 ADR 不删除。决策过期时,用新 ADR 标注 Supersedes,而不是覆盖历史。
- CI 做软提醒。改动 schema、IDL、核心配置时提示"是否需要 ADR",不要一开始就硬卡。
结语
ADR 本身没有变。变的是代码库的读者。
过去,它写给某个不确定的未来同事,所以收益遥远,靠自觉维持。现在,AI Agent 成了代码库里的高频读者。它不会去工位问作者,也不会天然理解团队历史。它只能读到仓库里被写下来的东西。
所以,AI 时代的 ADR 不再只是"给后来人留个交代"的工程美德,而是让 Agent 正确协作的上下文基础设施。
代码需要告诉机器怎么运行,也需要告诉机器为什么这样运行。ADR 的价值,正是在这里被重新激活。
推荐阅读
Claude Tool Search 深度拆解:延迟加载、工具引用和与 Codex 对比
Agent 评测别把「调优 Loop」 跑成「刷题 Loop」