尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

ADR架构决策记录:让技术选型与团队协作更高效

ADR架构决策记录:让技术选型与团队协作更高效 提到 ADR很多人的第一反应可能是美股市场里的 American Depositary Receipt。但在软件工程的语境里ADR 的完整含义是 Architecture Decision Record架构决策记录。这个东西在像 Uber 这样的大型技术团队中尤其值得关注因为系统越复杂决策历史越重要靠口头解释根本撑不过半年。我把它单独拿出来写是因为它是少数投入成本低、却很能防止团队反复争论同一件事的实践。适合读这篇文章的人不只是架构师还包括正在负责技术选型、需要给新团队建立规范和想让项目更可持续维护的开发者。最值得关注的点是ADR 的核心不是“写文档”而是给架构决策一个可检索、可引用、可更新的编号。1. 为什么要把“架构决策”本身当作交付物1.1 先分清 ADR 和架构设计文档架构设计文档一般描述系统应该长什么样包含模块划分、调用关系、数据流、部署拓扑。它的阅读对象是正在做实现的开发人员所以通常图文并茂。ADR 不一样。ADR 只记录一个时间点上的决策结果我们当时遇到了什么问题考虑过哪些方案最终选了哪一个为什么没有选其他方案。它不负责把系统全貌画出来也不承担操作手册的功能。这个差异很重要。如果团队把 ADR 当成架构设计文档来写一定会出现两个结果第一文档越来越长因为要描述的东西太多第二维护成本越来越高因为实现一变文档就得跟着改。ADR 一旦把重心放在“决策”而不是“图纸”上反而稳定得多因为一旦合并它就是历史记录代码可以重写决策也已经发生了。1.2 没有 ADR 时真实团队会发生什么我经常看到这样的场景一个新模块需要做技术选型团队花了两周对比方案最后定了一个结论。三个月后另外一个组在类似场景下重新选了完全不同的方案。两边各有道理但没有人能说清差异到底来自约束变化还是来自评审标准的随机性。更常见的情况是新人加入后问一句“为什么系统要这么设计”老人只能靠记忆回答。如果记忆清晰还能讲出背景如果不清晰就会出现各种“据说”“好像是”。这种信息损耗对小型项目不明显但参与的人一多、模块一多成本就很可观。ADR 解决的就是这类问题把决策上下文变成团队资产。之后任何讨论都可以直接引用一条记录说“这个问题在 ADR-0012 里已经评估过当时的约束是……”。这不是用文档压人而是让讨论从重复开始变成继续推进。1.3 哪些决策才值得写 ADR不是所有决定都值得记录。我一般会用两个标准判断影响面是否跨模块以及是否难以低成本撤回。如果只是把某个内部函数的命名从 getData 改成 getDetail不需要写 ADR。如果是引入一个新的状态管理方案、调整服务拆分边界、改变数据一致性策略、定义一个跨团队都要遵守的接口规范那就值得写。判断时还可以看备选方案之间是否互相排斥。多个方案可以在未来并行存在通常不是高风险决策只能取一个、选完之后很难反悔的决策才是 ADR 的目标。1.4 ADR 和架构文档的分工ADR 不能替代架构文档。架构文档负责呈现现状ADR 负责解释现状为什么变成这样。两者可以配合使用。我比较推荐的做法是在架构文档里涉及某个关键设计时直接链接到对应 ADR。例如“该模块采用事件驱动架构选择原因和约束见 ADR-0023”。这样读者既能看到架构图也能顺着链接回溯到决策背景对新人特别友好。2. 一份 ADR 的基本结构和编写顺序2.1 四个核心字段不要省不同团队的 ADR 模板差别很大但最核心的内容通常只有四个部分状态、背景、决策、后果。状态用来表示这条 ADR 当前处于什么阶段。它不是装饰字段而是让后来者一眼看出“这个决策现在还生效吗”。常用的状态包括 Proposed、Accepted、Deprecated、Superseded。其中 Rejected 状态也可以保留用于记录“我们评估过但没采用”的方案避免未来重新走弯路。背景描述触发这次决策的原因。背景里要写清问题、约束条件、时间因素但不要写成长篇项目综述。比如“当前配置依赖人工修改发布前经常出现环境串配置”这就足够。决策是 ADR 的核心。它必须是一个可执行、可检查的选择不能只写“采用更合理的方案”。正确的写法是“采用 Spring Profiles 独立配置目录构建时通过 profile 激活环境相关配置”。这样后续维护人员才能判断代码是否符合决策。后果用来记录引入该决策后的收益、成本、风险和验证方式。很多人只写优点不写风险这会让后果部分失去价值。风险不是失败而是团队已经意识到的代价。2.2 一个可以直接复制使用的最小模板下面这份模板是我在实际项目中比较常用的简化版。它不追求覆盖所有场景但足够支撑大多数技术决策记录。# ADR-0012引入多环境配置分离方案 - 状态已接受Accepted - 日期2025-06-01 - 决策者后端团队、运维负责人 ## 背景 当前配置全部写在 application.yml环境切换靠注释完成 导致发布前频繁改动且容易把测试配置带到生产。 ## 决策 采用 Spring Profiles 独立配置目录的方式 每个环境一个配置片段构建时通过 profile 激活。 ## 后果 - 收益环境切换不再修改代码降低发布风险。 - 成本需要梳理一遍现有配置项增加少量维护工作。 - 风险配置文件数量增多需要统一命名规范。 ## 备选方案 - 使用环境变量全覆盖不采用因为本团队部署平台对部分变量管理不友好。 - 使用配置中心延后评估当前规模暂不引入外部依赖。模板不要一开始就做得很重。字段一多写的人会焦虑读的人也会失去耐心。我建议先用这个最小模板跑三到五次真实决策再根据团队情况增加“相关人”“外部链接”“验证计划”等字段。2.3 好 ADR 和坏 ADR 的区别判断一份 ADR 写得好不好有几个很具体的方法。第一一个不熟悉项目的开发人员能不能在十分钟内读完并回答出“最终选了什么、为什么选它”。如果读完后还要追问背景说明背景和决策写得不够清楚。第二后续遇到类似讨论时这条 ADR 能不能被直接引用。如果内容太模糊引用它也无法终止重复讨论价值就大打折扣。第三备选方案是不是认真写出来的。真实决策一定考虑过其他选项即便有些选项很荒谬也要写清楚“为什么被排除”。这个字段是防止后人重复踩坑最重要的信息。我可以把好的 ADR 与坏的 ADR 做一个小对比维度好的 ADR坏的味道长度一页以内数页设计文档状态随生命周期更新写完就不管决策明确选择和不做什么“采用更优方案”备选方案记录真实评估过程凑数或缺失引用方式有唯一编号靠标题搜索2.4 状态流转要及时维护ADR 不是一次性文件。决策会被替换、会被否决、也会在某个版本后失效。状态维护是必须的。常见的流转路径是Proposed 表示提议评审通过后变成 Accepted如果评审后没有采纳可以标记为 Rejected如果旧决策被新决策取代旧 ADR 标记为 Superseded并在顶部写明“被 ADR-0028 取代”。每次状态变化都修改日期不要只保留创建日期。这个动作看起来很简单但它是团队能否真正使用 ADR 的关键。状态不及时更新的 ADR 库最终会变成没有可信度的历史垃圾堆。3. 在团队里落地 ADR从单条记录到决策库3.1 先定位置和命名规则ADR 的存储位置最好跟着代码走而不是放在一个没人访问的内部 Wiki 深层目录。最常见的做法是在仓库根目录下创建docs/adr/每个 ADR 一个 Markdown 文件。命名规则建议用递增编号加短标题例如0001-模块边界调整.md、0002-引入事件驱动.md。编号固定后别人就可以在代码注释、评审记录、会议纪要里直接引用“ADR-0012”。不要用“2025-06-01-多环境配置.md”这种带日期的命名因为后面引用时还需要查日期不够方便。如果仓库很多可以单独建一个架构仓库放置所有团队的 ADR。但要注意这个仓库必须和日常开发评审流程打通否则容易变成无人区。顺序上还是先在单个仓库里跑通再考虑跨仓治理。3.2 走一次完整的评审流程ADR 应该像代码一样进入版本管理并走评审。最简单的流程是基于主分支创建一个分支把 ADR 文件提交上去发起合并请求相关人评审通过后合并。评审时重点关注几个点背景是否描述了真实的约束而不是为了给结论找理由。决策是否清晰可执行能否作为后续代码评审的依据。后果是否包含成本和风险而不仅仅是收益。备选方案是否写清楚了排除原因。是否会与已有 ADR 冲突如果会优先解决冲突而不是直接合并。这里不要陷入过度评审。ADR 是记录决策不是收集所有人的投票。只要关键干系人确认背景、决策、后果都清楚就可以先合并。后续如果发现偏差可以再升级状态或重新决策。3.3 让 ADR 出现在日常开发动作里大多数 ADR 没人看不是方法不好而是它没有和日常流程发生关联。想让 ADR 真正流转起来可以分四步。第一步在合并请求模板里加一个字段“本改动是否受某条 ADR 影响如有请引用编号。”这个简单的强制项会让开发者在写代码时回头翻 ADR。第二步在关键代码处写注释解释特殊边界。例如“这个接口之所以保留是因为 ADR-0007 约定兼容旧客户端不能删除”。这种注释比直接删代码更安全。第三步在架构文档和项目 README 中做索引把当前有效的 ADR 列出来避免新人从零开始翻目录。第四步在技术评审会前把相关 ADR 链接到会议议题里。准备参会的人先读一遍效率会明显提升。3.4 从单条记录积累成决策库当我看到团队里已经有十几条 ADR 时会更关注索引和关联关系。例如在docs/adr/README.md里维护一张表编号标题状态创建时间0001模块边界调整已接受2025-01-100002引入事件驱动已被 0007 取代2025-02-150003数据库分库方案已接受2025-03-01这张表不需要自动生成手工维护在早期更灵活。通过索引ADR 才能从一份份孤立文档变成团队可检索的决策库。4. 大规模团队实践里的边界与避坑清单4.1 不是所有技术决定都值得写成 ADRADR 用得好是知识沉淀用得不好就是流程负担。最重要的一条边界是影响面小、可逆性强的决策不写 ADR。比如调整一个模块内部的日志级别、优化一段循环逻辑、更新依赖的 patch 版本这些都不需要。写了反而给团队增加负担大家很快就对 ADR 失去耐心。反过来跨团队都要遵守的接口规范、影响数据存储方式的变更、需要迁移大量代码的方案、要维护很长时间的兼容策略这些都应该写 ADR。我个人的判断顺序是先问“这个决策会影响别人吗”再问“做错了能低成本撤销吗”如果影响面局限在单个模块而且可以随时回退就不值得写。只有在影响面跨模块或难以回退时才需要进入 ADR 流程。4.2 三个容易被忽视的坑第一个坑是只写新决策不更新旧状态。很多团队刚落地 ADR 时热情很高新方案写了一条又一条但旧的已经被替代的决策仍然显示 Accepted。后来者查文档时会被误导以为旧方案还是当前标准。正确的习惯是每次新增 ADR 时都要检查一下它是否影响已有 ADR如果有影响同时更新旧 ADR 的状态。第二个坑是把 ADR 变成设计文档。这里要再次强调ADR 的核心是决策不是全量设计。设计文档可以讲系统架构、模块划分、接口定义ADR 只需要回答“这个决策怎么来的”。如果每条 ADR 都写几千字团队很快就坚持不下去。第三个坑是没有统一引用方式。有人用文档名引用有人用标题引用还有人在评论里说“之前讨论过”。这会让 ADR 库散乱。好的做法是只用编号引用并且编号稳定不变。4.3 当一条 ADR 不再适用时怎么处理决策不是永恒的。业务变了、团队变了、技术约束变了旧决策自然会失效。正确的处理方式是保留旧 ADR但它标记为 Superseded同时新建一条 ADR 说明新决策。例如旧 ADR 说“当前规模暂不引入配置中心”一年后业务增长配置中心成为必要。这时候不修改旧 ADR 的正文而是新写一条 ADR说明为什么之前不引入、现在为什么要引入并在新 ADR 中引用旧 ADR。旧 ADR 状态改成“已被 ADR-0031 取代”。这样做的好处是历史完整。后来人能看到系统的演进路径什么时候决定不做什么什么时候改变了主意改变的原因是什么。这种信息对架构治理很有价值。如果发现 Superseded 的 ADR 数量快速增加说明系统处于剧烈变化期。可以先做一次架构复盘判断是业务需求变化太快还是前期决策质量太低。不要去批评当时的决策而是找到导致频繁变化的共性原因再决定后续在评审环节加什么约束。4.4 团队不看 ADR 时的排查链路如果已经建立了 ADR 机制但团队根本不看不要急着追加更多流程按下面顺序排查。先看位置。ADR 是不是放在普通开发者日常能接触到的仓库里如果放在个人网盘、内部 Wiki 深层目录或者一个从不看的小仓库里那问题出在入口上应该把 ADR 移到和代码评审相同的平台。再看模板。模板是不是要求填写太多字段如果写一条 ADR 需要半天大家就会应付了事。模板应该短到让人愿意写。再看评审。ADR 评审有没有实际讨论如果每次都是“OK 合并”说明决策者没有认真参与记录自然也不被信任。再看引用。代码合并时是否强制检查“本次改动受哪条 ADR 影响”如果没有这个动作ADR 和实际开发就处于两个世界。最后看状态。是不是大量 ADR 过期没更新如果状态字段不可信那么整个库的可信度都会下降。这套排查链路适用于大多数团队。通常只要把入口、模板、评审、引用、状态这五件事中两件理顺ADR 就会被真正用起来。4.5 分阶段推进不要一步到位如果团队还没有任何 ADR我建议不要直接搭建一个复杂的决策管理系统。先从一个很小的闭环开始。第一步由最近一次有争议的技术讨论的发起人写一条 ADR使用最小模板拉相关人评审合并到仓库。第二步跑通两条后再在 docs/adr 下建立 README 索引统一定义状态和命名规则。第三步跑一个月后把“是否受 ADR 影响”加到合并请求模板里。第四步再考虑工具化、自动化状态提醒等更重的方案。这样推进的好处是每一步都能快速看到效果团队不会因为流程太重而反弹。回到开头的判断。ADR 的真正价值不是把“uber / ADR”当成一条金融代码去联想而是让技术团队在越来越复杂的环境里依然能把架构决策讲清楚。我比较建议的做法是先把最小模板用起来连续记录三条真实决策再回头评估团队是否出现了“引用决策而不是争论方案”的变化。只要这个变化出现ADR 就算真正落地了。
返回列表