半年后没人记得"当初为什么这么选",于是新人推倒重来,把当年否掉的坑重新踩一遍。ADR 就是治这个病的。
代码记录了系统"是什么",但"为什么是这样"只存在于当事人脑子里,而人会离职、会遗忘。Michael Nygard 2011 年提出 ADR(adr.github.io):每个重大架构决策写一页纸,进代码仓库,跟代码一起演化。Nygard 格式只有五节:
# ADR-007: 竞价裁定采用单写入点串行裁定 ## 状态 已接受(2026-07-30) ## 背景 驱动属性排序中一致性居首:截止瞬间必须产生全局唯一 成交结果,出错直接引发资金纠纷。备选方案: A) 单写入点串行裁定 B) 分布式锁 C) 乐观并发+冲突重试 ## 决策 采用 A:所有出价路由到单一裁定服务实例,按到达序串行裁定。 ## 后果 + 唯一成交有结构性保证,无分布式一致性协议的复杂度 - 写入吞吐受单点上限约束(压测目标 5000 TPS,当前够用) - 故障转移窗口(≤5s)内不可写,需配合出价缓冲队列 - 否掉 B:锁服务自身的可用性成为新依赖; 否掉 C:重试风暴在截止瞬间不可控
三个写作要点:
ADR 记决策,但新人还需要一份"系统总览"。arc42 给出 12 节标准骨架,官方态度就是按需裁剪——填满 12 节不是规范,是灾难。推荐最小集(7 节):
| arc42 节 | 写什么 | 对应本课程 |
|---|---|---|
| 1. 目标与质量要求 | 驱动属性排序 + 质量属性场景 | 第 1 课产物 |
| 2. 约束 | 技术/组织/合规约束 | 第 1 课产物 |
| 3. 上下文 | C4 上下文图 | 第 4 课产物 |
| 4. 方案策略 | 风格选择与理由(一段话) | 第 2 课产物 |
| 5. 构建块视图 | C4 容器图(+ 关键组件图) | 第 4 课产物 |
| 9. 架构决策 | ADR 索引 | 本课产物 |
| 11. 风险与技术债 | 评审的风险清单 | 第 3 课产物 |
注意:整个课程每一课的产物,恰好填进这份骨架——方法论走完,架构文档自动成形,不存在"设计完了再补文档"这回事。
docs/adr/,文件名 NNNN-短横线标题.md,顺序编号不重用。你的任务(10 分钟):用 Nygard 格式为拍卖系统写一篇 ADR,题目:"围观推送采用事件驱动最终一致通道"。要求:背景引用你的驱动属性排序;至少列 2 个备选;后果栏必须有减号;把第 3 课挖出的"围观者可能基于旧价出价"风险体现在后果里。
# ADR-008: 围观推送采用事件驱动最终一致通道 ## 状态 已接受(2026-07-30) ## 背景 驱动排序:一致性 > 可用性 > 伸缩/低延迟。数万人围观的 扇出压力不能回压竞价核心(一致性优先的单写入点)。 备选:A) 事件总线+推送集群 B) 围观者轮询API C) 推送与裁定同库同步写 ## 决策 采用 A:裁定服务发布价格事件到总线,独立推送集群 订阅并经 WebSocket 扇出,允许亚秒级延迟。 ## 后果 + 扇出与裁定解耦,围观规模水平扩展,不威胁一致性 + 削峰:总线吸收截止前的事件洪峰 - 最终一致:围观者可能看到旧价并据此出价被拒, 需前端明示"出价以服务端裁定为准"(对应风险 R-02) - 消息总线成为推送链路单点,需给出总线故障降级方案 (降级为 B 的轮询,另立行动项) - 否掉 B:数万人轮询对 API 是自伤式压力; 否掉 C:同步写扇出会把围观负载灌进裁定路径, 与第一驱动属性直接冲突
检查你的版本:背景有没有排序?备选有没有否决理由?减号是不是具体到可验证(而非"有一定复杂度"这类空话)?三项都有,就是合格的 ADR。
主推资料源:ADR 模板与示例集(GitHub)——Nygard 原始模板加几十种变体与真实案例;adr.github.io 是社区权威站。arc42 各节写法见官方文档,模板速查见文档化速查。
💬 写好的 ADR 贴回对话,我按三项检查逐条点评。