“为什么这里用的是文件对接?直接查数据库不就行了吗?”——打开接手系统代码的开发者,几乎必然会撞上这类疑问。而且在大多数情况下,知道答案的人早已不在项目里了。
当初一定是有理由的。也许是没有获得直接连接对方系统数据库的许可,也许在当时的交期条件下,那是唯一安全的方式。但如果这个理由没有被保留下来,后任者要么在”不知道能不能动”的代码前止步不前,要么反过来连同理由一起把它破坏掉。
在本博客中,我们曾在「委托开发 Windows 应用程序前该梳理的事项」中讲解过委托开发的推进方式,也在「从 IPA《示范交易与合同》学习准委托合同与承揽合同的区分」中讲解过合同的框架。本文要谈的是更进一步的话题——”做完之后,如何让系统撑过之后的好几年”。我们将以小规模的委托开发、企业内部开发为前提,讲解一种以最小成本保留设计判断理由的机制——ADR(Architecture Decision Record)。
1. 先说结论
- 应该先于面面俱到的设计文档,保留下”决定的记录”。因为在维护中真正让人为难的,不是不知道”在做什么”,而是不知道”为什么这样做”。
- ADR 是一种轻量级的记录形式,把一个决定,按照”标题 / 状态 / 背景 / 决定 / 结果”的固定格式写在一个文件里。由 Michael Nygard 于 2011 年提出,原则上每篇控制在 1〜2 页以内。1
- 存放位置是与代码相同的仓库(例如
docs/adr/0001-title.md)。不放在 Wiki 或共享文件夹中,而是与代码一起进行版本管理,并与代码评审一起查看。12 - 决定不会被覆盖。要改变方针时,新增一份 ADR,并把旧 ADR 的状态改为 Superseded(已被替代),二者相互引用。ADR 是一份只增不改的日志。2
- 不是什么都写,而是只写”事后难以更改”“存在多个合理选项”“受制约因素左右”的决定。命名规则、格式化工具设置等不在此列。2
- 根据笔者的经验,把篇幅控制在每篇 15〜30 分钟能写完的量,是能够坚持下去的前提条件。厚重的模板往往写到第 3 篇就会停摆。
- 在委托开发中,ADR 会成为可以与发包方共享的成果物。它可以直接当作验收时的说明材料,以及负责人变更、供应商变更时的交接资料来使用。
2. “不知道『为什么会这样』”的问题
2.1 代码讲的是 What,不讲 Why
读代码的话,”在做什么”(只要花时间)是能弄明白的。搞不明白的,是下面这类”为什么”。
- 为什么数据库用的是 SQLite 而不是 SQL Server
- 为什么和其他系统的对接用的是 CSV 文件传递,而不是 Web API
- 为什么只有这份报表要启动 Excel 来打印
- 为什么还停留在 .NET Framework,没有升级到现行的 .NET
这类决定背后,必定存在着当时的预算、交期、客户方面的制约、与既有资产的权衡取舍等存在于代码之外的理由。这些理由写进注释里显得太大,写进设计文档里,”决定的来龙去脉”又显得不合适。结果就是,理由哪里都没有留下。
2.2 存放决定的地方,大多在数年后就会消失
那么实际上,设计判断的理由现在都存放在哪里呢?我们来比较一下常见的存放地点。
| 存放位置 | 数年后是否还留存 | 与代码的距离 | 后任能否找到 |
|---|---|---|---|
| 口头 / 会议中达成的共识 | 不会留存 | ─ | 不可能 |
| 聊天记录(Teams/Slack) | 被刷走,实质上消失 | 远 | 几乎不可能 |
| 邮件 | 埋没在个人收件箱中 | 远 | 随离职而消失 |
| 会议纪要(共享文件夹) | 会留存,但良莠不齐 | 远 | 不知道”是哪一次会议的纪要” |
| Wiki・设计文档 | 更新停滞,逐渐与现状脱节 | 远 | 能找到,但不可信 |
| ADR(仓库内) | 与代码一同留存 | 同一仓库 | 打开 docs/adr/ 即可 |
Microsoft 的架构指南也明确指出,没有被记录下来的决定会被遗忘,进而招致同样的讨论被反复翻出,以及违背最初意图的变更。2
2.3 在委托开发中,合同的分界点就是记忆的分界点
如果是自研开发,”问那个人就知道了”这种做法还能管用一阵子;但在委托开发中,除了负责人的调动、离职之外,还会发生供应商变更。一旦开发方和维护方变成了不同的供应商,原本存在于口头和聊天记录中的”为什么”就会彻底消失。
从合同的角度来看,开发与维护是分开的合同、分开的工序,这本来就是常态(这一结构我们在「IPA《示范交易与合同》解说文章」中处理过)。另外,正如在「准委托合同下的正确工作方式」中整理的那样,正因为在准委托合同下,受托方是自主推进业务的,能够向发包方展示”做了什么判断、如何判断”的记录,就成为了信任的支撑。ADR 在这两方面都能发挥作用。
3. 什么是 ADR
3.1 Nygard 的提案 —— 5 个要素与 2 页上限
ADR 是 Michael Nygard 在其 2011 年的博客文章《Documenting Architecture Decisions》中提出的形式。1 要点如下。
- 一个决定对应一个文件。按顺序编号,编号不重复使用
- 文件采用 Markdown 等轻量格式,放在项目的仓库内
- 结构由标题 / 状态 / 背景 / 决定 / 结果(Consequences)这 5 个要素组成
- 状态从提议中(proposed)推进到已批准(accepted),推翻时改为已废弃(deprecated)或已被替代(superseded)。旧记录不会被删除
- 整体控制在 1〜2 页以内。用完整的文字来写,让未来的开发者读起来就像在对话一样
虽然名字里带着”架构”两个字,但它并不是专供大型系统使用的方法。恰恰相反,正是在没有专职架构师、也没有专人负责文档的小规模开发中,这种”最小化的固定格式”才最能发挥作用。此外,ADR 的模板与工具已经在社区网站(adr.github.io)上得到了系统的整理,可以作为理解”把架构上重要的决定连同依据与权衡一起记录下来”这一思路的入门参考。3
3.2 Markdown 模板
下面是笔者在小规模项目中使用的模板,完全沿用 Nygard 形式的最小结构。
# ADR-NNNN: (决定内容用一句话简要概括)
## 状态
提议中 | 已批准 | 已废弃 | 已被替代(→ ADR-MMMM)
## 背景
为什么需要做出这个决定。请写清楚技术与业务上的前提条件、
制约因素(预算、交期、既有资产、客户环境)、考虑过的备选方案,
让不了解当时情况的读者也能看懂。
## 决定
用主动语态、斩钉截铁地说"要做……"。1〜3 句话。
## 结果
写出这个决定带来的好处和坏处(权衡取舍)两方面。
如果有促使将来重新审视这个决定的条件,也一并写出。
关键在于,”结果”部分也要写出不好的一面。没有权衡取舍的决定,几乎没有记录的价值。Microsoft 的指南也强调,不要有意或无意地隐藏决定带来的后果,没有依据的记录会随着时间推移而失去价值。2
4. ADR 中该写什么、不该写什么
ADR 无法坚持下去的最大原因,是”想把什么都写进去”。Microsoft 的指南中提到,应把记录范围限定在会影响系统结构或重要质量特性、且事后难以撤回的事项上。2 把这一点落实到日常判断中,就是下面这张表。
| 决定的类型 | 例子 | 是否写入 ADR | 理由 |
|---|---|---|---|
| 事后难以更改的技术选型 | 把数据库定为 SQLite、把通信方式定为文件对接 | 写 | 变更成本高,不知道理由就去动它很危险 |
| 从多个合理选项中做出的选择 | 报表不用 COM 对接,改用库来生成 | 写 | “为什么放弃了另一种方案”能缩短后任重新论证的时间 |
| 制约因素起了决定作用 | 因为客户环境是离线的,所以放弃了自动更新 | 写 | 当制约消失时(环境更新时)可以重新审视 |
| 与外部达成的约定 | 把 CSV 的字符编码、格式对齐到对方的规范 | 写 | 可以明确标示出这是一个自家公司无法单方面更改的边界 |
| 规范・风格的统一 | 命名规则、格式化工具、using 语句的排列顺序 | 不写 | .editorconfig 等配置文件加自动化就足够了 |
| 随时都能更改的实现细节 | 内部类的拆分、private 方法的组织方式 | 不写 | 代码本身和代码评审就足够了 |
| 常规运维工作 | 库的补丁版本更新 | 不写 | 变更历史(提交日志)就足够了 |
拿不准的时候,判断标准只有一个:“一年后看到这段代码的人(包括你自己)会不会想问『为什么?』”。会想问,就写;只要看代码或配置就一目了然的,就不写。
另一个容易踩的坑,是用”文档的种类”来考虑该写多细。像下面这样事先划分好各自的角色分工,就不会再犹豫。
| 想保留的信息 | 适合的位置 | 与 ADR 的关系 |
|---|---|---|
| 为什么选择了这种方式 | ADR | 主体内容 |
| 当前的架构图・数据流向 | 设计文档(薄薄的一份) | 从 ADR 引用 |
| 每次具体变更的内容 | 提交信息 / PR | 写上 ADR 编号进行关联 |
| 操作步骤 | 操作手册 | 是另外一回事(读者不同)。写法可参考「Word 手册制作的基本原则 - 常见误区与改进方法」 |
| 故障处理的记录 | 故障单・issue | 如果处理结果导致方式发生变化,就据此新建一份 ADR |
5. 小规模委托开发中的运用
5.1 目录与文件命名
在仓库根目录下建立 docs/adr/,用”序号 + 简短 slug”的方式命名文件存放。
docs/
adr/
0001-record-architecture-decisions.md
0002-use-sqlite-for-local-storage.md
0003-excel-report-via-com-automation.md
0007-excel-report-via-openxml-library.md
惯例做法是,把第一篇 ADR 就写成”决定要使用 ADR”这件事本身。这样一来,后任只要看一眼 docs/adr/,就能把运用规则一并搞清楚。
5.2 何时写、由谁评审
- 书的时机是”刚做完决定之后”。作为设计讨论的收尾,要在当天就把会议的结论写成 ADR。后面会提到,攒到之后再一起写是会失败的。
- 把 ADR 纳入代码评审。只需要检查涉及方式变更的 Pull Request 中是否包含了 ADR 的新增或更新即可,仅此而已。不需要专门为 ADR 设立审批会议,把它变成评审的一部分,才是小规模团队现实可行的做法。把 ADR 纳入版本管理,Microsoft 的指南中也对此表示推荐。2
- 要推翻一个决定时,写一份新的 ADR,并把旧 ADR 的状态改为
Superseded,两者相互引用。正文不做改写。不去编辑已批准的记录,而是通过替代关系的链条来保留历史──这就是把 ADR 当作”只增不改的日志”来对待的含义。2
5.3 作为与发包方共享的成果物的 ADR
在委托开发中,我建议把 ADR 作为交付物的一部分与发包方共享。这样做有三个好处。
- 成为验收与说明的材料。不必再口头解释”为什么是这种架构”,只要把 ADR 拿出来给对方看就够了。对于那些由制约因素(预算、交期、环境)决定的选择,发包方自己就是当事人,有了记录,就能防止日后出现认识上的偏差。
- 成为供应商变更时的保险。站在发包方的立场,有没有一份能交给下一任供应商的”判断历史”,会让交接的成本和风险大不相同。关于下单前的梳理,我们已经在「Windows 应用程序委托开发指南」中写过;而在签订合同时决定”交付之后应该留下哪些文档”这一环节上,ADR 属于性价比最高的一类。
- 与准委托合同下的汇报很契合。在准委托合同中,需要对业务执行情况进行汇报,而设计阶段的汇报,可以直接拿 ADR 来用。
5.4 所需时间的真实感
根据笔者的经验,按模板写一篇需要 15〜30 分钟。如果是小规模项目,决定的发生频率大约是每月几次,所以每月投入 1〜2 小时,就能把所有的”为什么”都保留下来。和数年后用于调查、重新论证、交接而消耗掉的时间相比,几乎找不到不划算的现场。
6. 常见的失败模式
| 失败模式 | 症状 | 对策 |
|---|---|---|
| 写得太多 | 连琐碎的决定都做成 ADR,三周就精疲力尽 | 用第 4 章的判断表来收窄对象。每月几篇才是正常状态 |
| 模板太厚重 | 附带审批栏、影响分析、风险评估的格式,结果没人愿意写 | 回归 Nygard 的 5 个要素就好。上限 1〜2 页1 |
| 写在 Wiki 里 | 在与代码分离的地方更新会停滞,逐渐脱节而失去可信度 | 放在仓库内,与 PR 一起评审 |
| 事后攒在一起写 | “等忙完了再写” → 到时候记忆已经消失,写不出来 | 做完决定之后立刻写。写不出来的话,就在做决定的现场一边共享屏幕一边写 |
| 改写过去的 ADR | 历史消失,搞不清楚”方针是什么时候变的” | 用 Superseded 来替代,保持正文内容不变2 |
| 不写结果(坏的一面) | 沦为单纯的决定通知,对日后重新论证没有帮助 | 必须写出权衡取舍以及”重新审视的条件” |
尤其是”事后攒在一起写”,在给既有系统中途引入 ADR 时特别容易踩这个坑。不要试图把过去所有的决定都还原出来,现实的做法是只回溯几篇自己已知的主要决定,其余的从今天以后的决定开始逐步积累。即便是既有(brownfield)系统,只要还掌握着过去的决定,回溯记录下来也是有价值的。2
7. ADR 实例
下面用小规模 Windows 业务应用中常见的题材,展示两篇完整的 ADR 实例(内容为一般化的示例)。
第一篇,是技术选型中的经典题材——数据库的决定。
# ADR-0002: 业务数据的存储方式采用 SQLite
## 状态
已批准 (2026-07-17)
## 背景
本系统是单一据点的库存管理桌面应用程序。使用者有 2〜3 名,
但实际运用中安装在办公室主负责人 PC 的 1 台设备上,轮流使用(不会同时使用)。
客户内部没有能够运维数据库服务器的人员,也没有购置服务器设备的预算。
预计即便运行 10 年,数据量也仅在数百 MB 左右。
作为候选方案,我们考虑过 SQL Server Express / SQLite / Access 文件(.accdb)。
SQL Server Express 因为客户方没有能够持续进行服务器搭建以及
Windows Update 之后运行确认的体制,所以予以放弃。Access 则因为
并发更新时的损坏风险以及未来的迁移性考虑,也予以放弃。
## 决定
数据存储采用 SQLite。数据库文件不放在共享文件夹中,
而是放在主负责人 PC 的本地。备份则每天通过 VACUUM INTO
生成快照并保存到 NAS(在运行中直接复制原始文件的方式不可取,
因为可能因 WAL 文件遗漏或写入竞争而导致备份损坏)。
## 结果
- 好处:无需搭建和维护数据库服务器。备份也只需一条 SQL 语句即可完成
- 好处:应用分发时可以内置运行库,安装变得简单
- 坏处:写入会以数据库为单位加锁,因此无法扩展到多据点、多人同时使用的场景
- 坏处:将来如果要迁移到服务器数据库,需要进行数据迁移和连接层的改造
- 一旦需要从多台 PC 同时使用,就应重新审视这个决定(届时应转为服务器数据库或经由 API 的架构)
第二篇,是推翻一次已做决定的例子。请留意其中 Superseded 的用法。
# ADR-0007: 报表的 Excel 输出方式,从 COM 对接改为库生成
## 状态
已批准 (2026-07-17) ── 替代 ADR-0003(采用 COM 自动化)
## 背景
存在把送货单和月度汇总以 Excel 文件形式输出的需求。
最初按照 ADR-0003 的决定,用 Excel 的 COM 自动化来实现,
但在无人值守执行的夜间批处理中,反复出现 Excel 进程残留导致
处理停滞的问题,而且执行用 PC 需要 Office 许可证这一点,
也在每次终端更新时都会成为问题。
作为候选方案,我们考虑过继续使用 COM 对接(追加进程监控)、
切换为直接生成 Open XML 格式的库、把报表改为 PDF(属于需求变更)
这三种方案。PDF 化因为交易对象需要在 Excel 中追加批注为前提,
所以不可行。
## 决定
报表改为用库直接生成 .xlsx 的方式。不再依赖 Excel 本体。
格式作为模板 .xlsx 文件纳入仓库管理,通过单元格填充来生成。
## 结果
- 好处:执行环境不再需要 Excel,无人值守执行变得稳定
- 好处:进程残留的问题从结构上被消除
- 坏处:无法使用 Excel 的全部功能,现有报表的部分格式需要简化
- 坏处:把现有报表模板化需要投入改造工时
- ADR-0003 标记为 Superseded,并附上指向本 ADR 的引用
相信只要读完这两篇,就能回答交接时必然会冒出来的疑问——”这个系统为什么没有服务器数据库”“为什么报表代码里有启动 Excel 的痕迹”。两篇加起来也就 1500 字左右,写下来花不到 1 小时。
8. 总结
- 在维护和交接中丢失了会让人为难的,不是 What,而是 Why。没有被记录的决定会被遗忘,进而招致讨论被反复翻出,以及违背原意的变更。2
- ADR 是一种”1 个决定 = 1 个文件、5 个要素、控制在 1〜2 页以内”的轻量记录形式。保持 Nygard 的原型不变,就能直接用于小规模开发。1
- 只写”难以更改”“存在备选方案”“受制约因素左右”的决定。规范与格式交给自动化处理,不作为 ADR 的记录对象。2
- 存放位置是
docs/adr/,评审与 Pull Request 一起进行。决定不覆盖,而是用 Superseded 来替代,保持历史不可变。12 - 在委托开发中,ADR 会成为验收时的说明材料、供应商变更时的交接资料,是对发包方同样有价值的交付物。
- 每篇 15〜30 分钟。请从下一次设计判断开始写第一篇;如果是既有系统,就先从回溯几篇自己已知的主要决定开始。
相关文章
- 委托开发与运维保守合同应该怎么签 - 从 IPA《示范交易与合同》学习准委托合同与承揽合同的区分
- 为了不构成伪装承揽的准委托合同正确工作方式
- 委托开发 Windows 应用程序前该梳理的事项
- Word 手册制作的基本原则 - 常见误区与改进方法
相关咨询领域
小村软件有限公司承接在设计评审中支持引入 ADR、对既有系统的设计判断进行盘点与文档化,以及着眼于交接与供应商变更的维护体制建设。
参考链接
-
Michael Nygard,Documenting Architecture Decisions。ADR 的原始出处(2011 年)。内容涉及标题 / 背景 / 决定 / 状态 / 结果这 5 个要素、从提议中 → 已批准 → 已废弃 / 已被替代这一状态流转、1〜2 页的篇幅、以按序号编号的文件形式放在仓库中,以及不删除旧决定、而是用 Superseded 等方式保留下来。 ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn,Maintain an architecture decision record (ADR)。Azure Well-Architected Framework 的指南。内容涉及把 ADR 当作只增不改的日志、不编辑已批准的记录,变更时用新记录替代并相互链接,把对象限定在影响系统结构、重要质量特性且事后难以撤回的决定上,需要包含背景、依据、权衡取舍、状态(Proposed/Accepted/Superseded),未被记录的决定会被遗忘并招致讨论反复与违背意图的变更,以及即便是既有工作负载,回溯记录同样有价值。 ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11 ↩12 ↩13
-
adr.github.io,Architectural Decision Records。ADR 的社区网站。内容涉及架构决定(AD)与架构上重要需求(ASR)的定义、ADR 是记录单一决定及其依据、权衡取舍与结果的一种方式,以及各类模板与工具的整理。 ↩
相关文章
共享相同标签的最新文章。可以围绕相近的主题进一步加深理解。
别忘了先定好「多少秒才算达标」── 用 IPA「非功能要求分级」梳理非功能需求
「速度太慢」「故障应对超出预期」等纠纷,大多源于没有事先决定好非功能需求。本文用发包方也能理解的语言,解析 IPA「非功能要求分级」的六大项目、分级表与模型系统的用法,以及切实可行的应用方式。
接手了没有源代码也没有文档的系统 ── 在不中断运行的前提下开展运维保守的实务步骤
本文整理在没有源代码、也没有文档的业务系统上开始运维与保守工作的实务步骤。涵盖对运行环境的保全与备份、可执行文件与数据库的盘点、从行为中还原规格,以及续用、封装、重建之间的判断。
委托开发 Windows 应用程序前该梳理的事项
在委托外包开发 Windows 应用程序之前,梳理现有软件改造、设备联动、COM/ActiveX、发布与更新、维护等需要注意的要点。
故障处理不止于恢复 ── 写给小型开发团队的 Postmortem(再发防止)实践模板
把故障处理停留在「修复、道歉,就此结束」,同样的故障就会反复发生。本文把 blameless postmortem 翻译给小型团队使用,整理出可在 1 小时内写完的模板、再发防止策略的强度判断表,以及实施的分级(triage)标准。
如何安全地为没有测试的遗留业务应用做修改 ── 特性化测试与重构实战
为了能安全地修改没有测试的业务应用,本文用 C# 示例讲解固定当前行为的特性化测试(黄金母版法)步骤、创建接缝(seam)的方法,以及不把重构与功能追加混在一起的运营规则。
相关主题
与本文相近的主题页面。以本文为起点,可以进一步了解相关服务和其他文章。
Windows 技术主题
汇整 KomuraSoft LLC 关于 Windows 开发、故障调查与既有资产活用文章的主题中心。
与本主题相关的服务
本文与以下服务页面相关联,欢迎从最接近的入口查看。
Windows 应用程序开发
支持包含常驻处理、设备联动、运行日志与可维护结构的 Windows 桌面应用程序。
技术咨询 & 设计评审
协助梳理设计方向、架构边界、生命周期责任,以及既有 Windows 资产的处理方式。
常见问题
汇总了咨询这一主题时常见的问题。
- 什么是 ADR(Architecture Decision Record)?
- 它是一种文档,把与软件结构相关的一个决定,按照「标题 / 状态 / 背景 / 决定 / 结果」这样简短的固定格式,记录在一个文件里。这是 Michael Nygard 于 2011 年提出的轻量级形式,基本原则是每篇控制在 1〜2 页以内,并以 Markdown 形式提交到与代码相同的仓库中。与面面俱到的设计文档不同,它专注于保留「为什么做出了这个选择」以及「舍弃了哪些选项」。
- ADR 中应该写什么,什么可以不写?
- 应该写的是:事后难以更改的决定(数据库、通信方式的选型,外部对接的格式等)、从多个合理选项中做出选择的决定、由预算、交期、既有资产等制约因素起决定作用的决定。反过来,像命名规则、格式化工具设置这类可以由工具或规约机械统一的事项,以及容易更改、只要看代码就够了的事项,就不需要写。拿不准的时候,可以用「一年后的自己会不会想问『为什么?』」作为判断标准。
- 想要更改决定时,可以直接改写过去的 ADR 吗?
- 不应改写,而是新增一份 ADR 来替代它。把旧 ADR 的状态改为 Superseded(已被替代),加上指向新 ADR 的引用,正文则原样保留。Microsoft 的指南中也建议,把 ADR 当作只增不改的日志来对待,事后不要编辑已批准的记录。这样一来,「方针是何时、为什么发生变化的」这段历史本身,就会成为交接资料。
- 如果已经有设计文档,ADR 是不是就没必要了?
- 两者的角色不同。设计文档展示的是「当前是什么结构(What)」,但「为什么选择了这种结构、舍弃了什么(Why)」通常不会被保留下来。而且面面俱到的设计文档很容易停止更新,数年后往往会与代码脱节。ADR 只需在每次决定时追加几百字,因此更新很少会停滞——即便设计文档过时了,「判断的理由」也能独立留存下来。在小规模开发中,把详细设计文档做薄、再搭配 ADR 一起使用,是比较现实的组合方式。
作者简介
本文作者的个人简介页面。
Go Komura
小村软件有限公司 代表
以 Windows 软件开发、技术咨询与故障排查为中心,擅长难以复现的故障调查,以及既有资产仍在运行的项目。