架构决策记录(ADR)
指导 Agent 用固定标题块记录「在何种约束下为何选 B 而非 A」,便于后人理解历史权衡而非只看当前代码。
ADR 回答的是「当时为什么这样定」,不是替代 README;与 issue/PR 链接即可,正文保持自洽。Agent 起草时应先对齐编号规则与存放路径,再填上下文与备选方案,最后写可验证的后果与复审条件。
何时写 ADR 与决策流
出现难以回滚或跨团队契约的选型(数据模型、公开 API、部署拓扑、合规边界)时立项;纯局部实现细节可用 PR 说明代替。触发写 ADR 的条件:
- 选择数据库类型、消息队列、认证方案等基础设施
- 公开 API 设计或协议选择(REST vs gRPC vs GraphQL)
- 跨服务数据模型变更或依赖方向调整
- 合规 / 安全边界决策(加密算法、数据驻留要求)
[ 张力信号:性能 / 成本 / 合规 / 可维护性 / 技能栈 ]
│
▼
[ 起草:背景约束 + 候选方案 + 推荐方案 ]
│
▼
┌──────────┴──────────┐
▼ ▼
[ 同步评审或异步评论 ] [ 需要 PoC / 基准? ]
│ │
└──────────┬──────────┘
▼
[ 决议:提议 / 已接受 / 推迟 / 否决 ]
│
▼
[ 落盘:编号 + slug 文件名 + 链接相关 ADR / issue ]
│
▼
[ 实施后:记录正负面后果与监控项 ]
│
▼
[ 若被取代:状态改为已取代,并指向新 ADR 编号 ]
ADR 目录结构与文件命名约定:
# docs/adr/ 目录结构
docs/
adr/
README.md # 说明编号规则与状态定义
0001-use-postgresql-as-primary-db.md
0002-use-kafka-for-event-streaming.md
0003-adopt-jwt-for-api-auth.md
0042-migrate-to-graphql.md # 状态:已取代 → 见 ADR-0051
0051-adopt-rest-with-openapi.md # 状态:已接受,取代 ADR-0042
# 状态流转示例(ADR front matter)
---
status: accepted # proposed | accepted | deprecated | superseded
date: 2025-03-15
deciders: [tech-lead, dba, backend-team]
supersedes: [] # 本 ADR 取代哪些旧 ADR
superseded-by: "" # 若本 ADR 被取代,填入新 ADR 编号
---
写作要点与状态
状态字段建议:提议、已接受、已取代、已废弃;被取代时链接新 ADR 编号,保持链条可追溯。
决策段应可执行:选中方案的关键配置或接口形态一笔带过;备选方案简述否决理由(成本、风险、团队技能)。
后果段区分正面、负面与需监控的指标;负面项应有对应缓解或「已知债务」登记位置。
- 编号:全局递增或按子系统前缀,团队统一即可。
- 审阅:Tech lead 或架构评审会签名人可选填。
- 过期:触发复审的条件(流量级、合规变更等)。
真实 ADR 片段示例(选择消息队列):
# ADR-0002: 使用 Apache Kafka 作为事件流平台
## 元数据
- 状态: 已接受
- 日期: 2025-04-01
- 决策者: [platform-team, backend-leads]
## 背景
订单服务需将事件实时推送给库存、物流和通知三个下游服务。
当前用 REST 回调,新增下游需改上游代码,耦合严重。
日峰值消息量预计 500 万条/天,需要持久化和回放能力。
## 备选方案
### 方案 A — Apache Kafka
- 优:高吞吐、持久化、消费者组独立偏移量、支持回放
- 缺:运维复杂度高,需要 ZooKeeper/KRaft
### 方案 B — RabbitMQ
- 优:运维简单,队列语义直观
- 缺:默认不持久化到磁盘;无回放;吞吐量在此规模下存疑
### 方案 C — 继续用 REST 回调
- 缺:强耦合,扩展下游需改上游;无消息持久化
## 决策
选方案 A(Kafka)。500 万/天峰值 + 回放需求使 Kafka 的运维成本合理。
关键配置:3 broker + replication.factor=3 + min.insync.replicas=2。
## 后果
### 正面
- 下游解耦,新增消费者无需改上游
- 消息持久化 7 天,支持回放和重处理
### 负面与缓解
- 运维复杂度:引入 Kafka Operator(Strimzi),云环境用托管 MSK
- 消息顺序:仅在分区内有序,按 order_id 做 partition key
### 需监控
- consumer_lag(消费滞后)> 10,000 告警
- broker disk 使用率 > 70% 告警
## 复审条件
- 若日均消息量超过 5000 万条,重评 partition 数量
- 若 Kafka 托管费用超过预算 20%,评估 Pulsar 替代
ADR 分节模板
下列 Markdown 骨架可直接复制进仓库 docs/adr/;按团队习惯可增删「合规」「相关链接」等小节。
---
name: adr-authoring
description: 起草架构决策记录 Markdown 骨架
---
# ADR-NNN:<简短标题>
## 元数据(可选 YAML front matter 或表格)
- 状态: 提议 | 已接受 | 已取代 | 已废弃
- 日期: YYYY-MM-DD
- 决策者: <角色或姓名>
- 取代: <若适用,链接 ADR-MMM>
- 被取代自: <若适用>
## 背景与问题陈述
- 业务或技术驱动是什么?
- 若不决策,最坏情况是什么?
- 范围:包含 / 明确不包含的边界
## 决策驱动因素
- 必须满足的约束(SLA、预算、合规、截止日期)
- 优先级排序(例如:正确性 > 成本 > 交付速度)
## 备选方案
### 方案 A
- 概要、依赖、估计成本与风险
### 方案 B
- …
## 决策
- 选定方案(A/B/…)及一句话理由
- 关键接口、配置或数据形态(足够让实现者对齐)
## 后果
### 正面
### 负面与缓解
### 需监控的指标或告警
## 复审与失效条件
- 何时必须重开本 ADR(流量阈值、监管变化、上游废弃等)
## 相关链接
- Issue / PR / RFC / 运行手册
标题 slug 与文件名
从标题中提取拉丁字母与数字片段,连成 kebab-case,便于与编号拼成 NNN-slug.md。纯中文标题若无拉丁片段则无法自动生成 slug,请在标题中保留英文关键词或单独填写短名。
生成与复制仅在浏览器本地完成,不上传。slug 规则:连续拉丁词/数字转小写并用连字符连接。
---
name: adr-authoring
description: 起草架构决策记录:模板、状态流转与目录约定
---
# 触发条件(以下任一 → 必须写 ADR)
- 选择数据库类型 / 消息队列 / 认证方案等基础设施
- 公开 API 设计或协议选择
- 跨服务数据模型变更
- 合规 / 安全边界决策
# 目录约定
- 存放路径:docs/adr/
- 文件名:{NNNN}-{kebab-slug}.md
- 编号:全局递增,4位补零(0001, 0002 ...)
# 必须章节
1. 元数据(状态 / 日期 / 决策者)
2. 背景与问题陈述
3. 决策驱动因素(约束 + 优先级)
4. 备选方案(每个含否决理由)
5. 决策(选定方案 + 关键配置)
6. 后果(正面 / 负面与缓解 / 监控指标)
7. 复审与失效条件
# 状态流转
- proposed → accepted(评审通过)
- accepted → superseded(被新 ADR 取代)
- accepted → deprecated(需求消失,不再适用)
- 禁止直接删除:已被引用的 ADR 必须保留并更新状态
# 与 Agent 协作
- Agent 起草初稿后,人工填写「决策者」与「复审条件」
- 不变量:范围与权衡由业务 / 架构师拍板,Agent 辅助文档化
- 链接到相关 ADR、issue 与 RFC