技术决策记录(ADRs) TechnicalDecisionRecords(ADRs)

技术决策记录(ADRs)是一种专家级框架,用于记录、跟踪和管理架构决策,以保存知识和提高企业项目中的决策质量。

架构设计 0 次安装 0 次浏览 更新于 3/5/2026

技术决策记录(ADRs)

概述

架构决策记录(ADRs)是轻量级文档,用于捕获重要的架构决策及其理由,提供历史记录,帮助团队理解过去选择的"为什么"。这项技能提供了一种系统化的方法来创建、管理和维护ADRs,减少重新讨论已解决决策的时间,并在团队成员变动时防止知识丢失。ADRs既是当前团队成员的沟通工具,也是理解系统演变过程的学习资源。

为什么这很重要

  • 保存知识:决策比团队成员更长久;ADRs防止人员更替时的知识丢失
  • 减少重辩:清晰的文档阻止团队反复回顾已解决的决策
  • 提高决策质量:书面决策迫使对替代方案和权衡进行深入思考
  • 便于入职:新团队成员快速理解架构选择,无需在历史中搜寻
  • 提供责任制:明确决策者和决策理由

核心概念

1. 决策重要性阈值

并非每个决策都需要ADR。当决策是:

  • 重要:影响系统结构或架构
  • 长期:有持久的后果或难以逆转
  • 昂贵变更:需要大量努力修改
  • 跨领域:影响多个团队或组件
  • 权衡重大:涉及重大的妥协或权衡
  • 先例设定:为未来决策建立模式

不要为以下情况编写ADRs:

  • 微不足道的实现细节(使用代码注释)
  • 容易逆转的决策
  • 团队惯例(使用风格指南)
  • 临时解决方案

2. ADR结构

标准ADR模板确保一致性和完整性:

# ADR-XXX: [简短标题]

## 状态
[提议中 | 已接受 | 已弃用 | 被ADR-XXX取代]

## 背景
问题是什么?什么因素驱动这个决策?
包括背景、约束和需求。

## 决策
做出了什么决策?具体且明确。

## 后果
正反两面的结果是什么?

### 正面
- 好处1
- 好处2

### 负面
- 缺点1
- 缺点2

## 考虑的其他选项
评估了哪些其他选项?

### 选项1: [名称]
- 优点:...
- 缺点:...
- 为什么拒绝:...

3. 版本控制和链接

通过ADR关系维护可追溯性:

  • 顺序编号:使用ADR-001、ADR-002等
  • 永不删除:标记为已弃用或被取代,不要删除
  • 链接相关ADRs:引用相关或依赖的决策
  • 取代:当ADR-010取代ADR-005时,链接它们
  • 版本控制:将ADRs保存在Git中与代码一起

4. 存储和组织

组织ADRs以便发现和维护:

在仓库中(推荐)

docs/adr/
├── 0001-use-postgresql.md
├── 0002-jwt-authentication.md
├── 0003-graphql-api.md
└── README.md

其他存储选项:

  • Wiki/Confluence(便于发现)
  • 专用工具(log4brains、ADR Manager)
  • 文档平台(Notion、GitBook)

快速开始

  1. 确定决策:判断决策是否达到重要性阈值(影响架构、难以逆转、长期影响)
  2. 创建ADR:使用标准模板记录决策、背景、替代方案和后果
  3. 起草内容:编写背景(问题、约束)、决策(具体选择)、后果(利弊)和考虑的其他选项
  4. 审查:团队审查ADR以收集反馈和验证理由
  5. 批准:一旦达成共识,将状态标记为"已接受"
  6. 实施:根据批准的ADR执行决策
  7. 跟踪:监控结果并更新ADR;如果情况变化,标记为已弃用
# ADR-001: 使用PostgreSQL作为主数据库

## 状态
已接受(2024-01-15)

## 背景
我们需要为我们的电子商务平台选择一个数据库。需求:
- 订单处理需要ACID事务
- 报告需要复杂查询
- 可扩展到100K+用户
- 库存管理需要强一致性
- 团队有SQL经验
- 预算限制(优先开源)

## 决策
我们将使用PostgreSQL作为主数据库。

## 后果

### 正面
- ACID合规性确保金融交易的数据完整性
- 丰富的查询能力支持复杂报告需求
- 成熟的生态系统有广泛的工具和扩展
- 强大的社区支持和文档
- 团队已经熟悉SQL
- 免费开源(无需许可成本)
- 出色的性能满足我们的预期规模

### 负面
- 垂直扩展限制(虽然对我们的需求足够)
- 与托管服务相比,设置高可用性更复杂
- 需要事先仔细设计模式

## 考虑的其他选项

### 选项1: MongoDB
- 优点:灵活的模式、水平扩展、JSON原生
- 缺点:一致性保证较弱、团队不熟悉NoSQL、对我们的结构化数据来说过于复杂
- 为什么拒绝:我们的数据高度结构化和关系化;ACID保证至关重要

### 选项2: Amazon DynamoDB
- 优点:完全托管、出色的可扩展性、可预测的性能
- 缺点:大规模时昂贵、供应商锁定、查询能力有限、学习曲线
- 为什么拒绝:成本问题和查询限制超过了可扩展性的好处

### 选项3: MySQL
- 优点:与PostgreSQL类似,广泛使用,性能良好
- 缺点:功能不如PostgreSQL丰富,JSON支持较弱
- 为什么拒绝:PostgreSQL在相同复杂性下提供更好的功能

## 注释
- 日期:2024-01-15
- 作者:Jane Smith
- 审核者:John Doe、Alice Johnson
- 下次审核:2025-01-15(或当扩展超过100K用户时)

生产清单

  • [ ] 评估决策重要性阈值
  • [ ] 使用标准模板
  • [ ] 背景清晰描述问题和约束
  • [ ] 决策具体且明确
  • [ ] 记录后果(正面和负面)
  • [ ] 考虑其他选项并列出优缺点
  • [ ] 包括拒绝理由
  • [ ] ADR由团队审查
  • [ ] 状态标记为已接受
  • [ ] 分配顺序编号
  • [ ] 在版本控制中保存ADR
  • [ ] 链接相关ADRs
  • [ ] 实施遵循ADR
  • [ ] 在代码注释中引用ADR
  • [ ] 安排审核日期(如适用)

反模式

  1. 过度文档化:为琐碎或容易逆转的决策编写ADRs浪费时间并使文档混乱
  2. 理由模糊:未能解释"为什么"使ADRs对于理解过去决策的用处减少
  3. 缺少替代方案:未列出考虑的选项使人无法知道是否正确评估了替代方案
  4. 不更新:未能在情况变化时更新ADRs会导致过时的指导
  5. 无审查流程:跳过团队审查破坏了集体智慧和认同的价值
  6. 组织不善:组织不善的ADRs难以查找和使用;建立清晰的结构和编号
  7. 更新旧ADRs:永远不要修改已接受的ADRs;创建新的来取代或弃用

集成点

  • 架构审查:在架构审查过程中创建和引用ADRs
  • 代码注释:在代码中引用ADRs以将实现与架构理由联系起来
  • 拉取请求:在PR描述中链接相关ADRs以提供上下文
  • 文档:在架构文档和wiki中包含ADRs
  • CI/CD:使用ADRs在自动化检查中验证架构决策
  • 工具集成:使用adr-tools、MADR或log4brains进行ADR管理
  • 回顾:在回顾中审查ADR结果以从决策中学习

进一步阅读


ADRs是什么以及为什么它们重要

ADRs是:

  • 捕获重要决策的轻量级文档
  • 架构选择的历史记录
  • 当前和未来团队成员的沟通工具
  • 理解系统演变的学习资源

为什么它们重要:

  • 知识保存 - 决策比团队成员更长久
  • 上下文共享 - 新团队成员理解"为什么"
  • 决策质量 - 写作迫使深入思考
  • 责任制 - 明确决策的所有权
  • 避免重复 - 不要重新审视已解决的决策

何时编写ADR

决策重要性阈值

当决策: ✅ 编写ADR:

  • 影响系统结构或架构
  • 有长期后果
  • 难以或昂贵逆转
  • 影响多个团队或组件
  • 涉及重大权衡
  • 为未来决策设定先例

不要编写ADR:

  • 琐碎的实现细节
  • 容易逆转的决策
  • 团队惯例(使用风格指南代替)
  • 临时解决方案

示例

决策 ADR需要? 为什么
选择PostgreSQL与MongoDB ✅是 难以改变,影响整个系统
使用JWT进行身份验证 ✅是 安全关键,影响所有服务
命名变量userIduser_id ❌否 琐碎,使用linter/style guide
向函数添加日志 ❌否 容易逆转
采用微服务架构 ✅是 主要架构决策

ADR结构和格式

标准模板

# ADR-001: [简短标题]

## 状态
[提议中 | 已接受 | 已弃用 | 被ADR-XXX取代]

## 背景
我们面临什么问题?什么因素驱动这个决策?
包括相关背景、约束和需求。

## 决策
我们做出了什么决策?具体且明确。

## 后果
这个决策的正面和负面结果是什么?

### 正面
- 好处1
- 好处2

### 负面
- 缺点1
- 缺点2

### 风险
- 风险1
- 风险2

## 考虑的其他选项
我们评估了哪些其他选项?为什么被拒绝?

### 选项1: [名称]
- 优点:...
- 缺点:...
- 为什么拒绝:...

### 选项2: [名称]
- 优点:...
- 缺点:...
- 为什么拒绝:...

## 参考
- 相关文档链接
- 讨论链接
- 原型链接

## 注释
- 日期:YYYY-MM-DD
- 作者:姓名
- 审核者:姓名

ADR示例

示例1:数据库选择

# ADR-001: 使用PostgreSQL作为主数据库

## 状态
已接受(2024-01-15)

## 背景
我们需要为我们的电子商务平台选择一个数据库。需求:
- 订单处理需要ACID事务
- 报告需要复杂查询
- 可扩展到100K+用户
- 库存管理需要强一致性
- 团队有SQL经验
- 预算限制(优先开源)

## 决策
我们将使用PostgreSQL作为主数据库。

## 后果

### 正面
- ACID合规性确保金融交易的数据完整性
- 丰富的查询能力支持复杂报告需求
- 成熟的生态系统有广泛的工具和扩展
- 强大的社区支持和文档
- 团队已经熟悉SQL
- 免费开源(无需许可成本)
- 出色的性能满足我们的预期规模

### 负面
- 垂直扩展限制(虽然对我们的需求足够)
- 与托管服务相比,设置高可用性更复杂
- 需要事先仔细设计模式

### 风险
- 随着流量增长,可能需要添加读取副本
- 模式迁移需要仔细规划
- 如果查询没有优化,可能会出现性能问题

## 考虑的其他选项

### 选项1: MongoDB
- 优点:灵活的模式、水平扩展、JSON原生
- 缺点:一致性保证较弱、团队不熟悉NoSQL、对我们的结构化数据来说过于复杂
- 为什么拒绝:我们的数据高度结构化和关系化;ACID保证至关重要

### 选项2: Amazon DynamoDB
- 优点:完全托管、出色的可扩展性、可预测的性能
- 缺点:大规模时昂贵、供应商锁定、查询能力有限、学习曲线
- 为什么拒绝:成本问题和查询限制超过了可扩展性的好处

### 选项3: MySQL
- 优点:与PostgreSQL类似,广泛使用,性能良好
- 缺点:功能不如PostgreSQL丰富,JSON支持较弱
- 为什么拒绝:PostgreSQL在相同复杂性下提供更好的功能

## 参考
- [PostgreSQL文档](https://www.postgresql.org/docs/)
- [数据库比较电子表格](link-to-internal-doc)
- [团队讨论线程](link-to-slack/email)

## 注释
- 日期:2024-01-15
- 作者:Jane Smith
- 审核者:John Doe、Alice Johnson
- 下次审核:2025-01-15(或当扩展超过100K用户时)

示例2:认证策略

# ADR-002: 使用JWT和刷新令牌进行身份验证

## 状态
已接受(2024-01-20)

## 背景
我们需要一个适用于我们API的身份验证机制,该机制:
- 与我们的React前端和移动应用一起工作
- 支持无状态API服务器以实现水平扩展
- 提供合理的安全性
- 允许在需要时撤销令牌
- 在安全性和用户体验之间取得平衡

当前情况:
- 多种客户端类型(Web、iOS、Android)
- 微服务架构
- 需要水平扩展
- 安全性很重要但不是超高安全性(不是银行/医疗保健)

## 决策
我们将实现基于JWT的身份验证,带有刷新令牌:
- 短命的访问令牌(15分钟)
- 长命的刷新令牌(7天)
- 刷新令牌存储在数据库中以供撤销
- 访问令牌是无状态的(不存储)

## 后果

### 正面
- 无状态访问令牌实现水平扩展
- 在Web和移动设备上无缝工作
- 行业标准方法,有良好的库支持
- 可以通过刷新令牌失效来撤销访问
- 减少了数据库负载(仅在刷新时击中DB)
- 明确区分认证和授权

### 负面
- 无法立即撤销访问令牌(必须等待到期)
- 需要管理刷新令牌的存储和轮换
- 比基于会话的认证稍微复杂一些
- 如果不小心处理,令牌可能会被盗
- 需要在客户端实现令牌刷新逻辑

### 风险
- XSS攻击可能盗取令牌(通过Web的httpOnly cookies缓解)
- 令牌重放攻击(通过短到期缓解)
- 刷新令牌盗窃(通过轮换和安全存储缓解)

## 考虑的其他选项

### 选项1: 基于会话的身份验证
- 优点:易于实现,立即撤销,熟悉模式
- 缺点:需要粘性会话或共享会话存储,水平扩展不佳,与多个客户端复杂
- 为什么拒绝:不符合我们的微服务架构和扩展需求

### 选项2: OAuth 2.0与外部提供商(Auth0、Cognito)
- 优点:完全托管,经过实战测试,包括MFA和社交登录
- 缺点:供应商锁定,持续成本,控制较少,对我们的需求来说过于复杂
- 为什么拒绝:希望保持控制并避免在此阶段产生持续成本

### 选项3: API密钥
- 优点:简单,无状态
- 缺点:没有用户上下文,难以轮换,没有到期,安全问题
- 为什么拒绝:不支持用户特定权限,缺乏安全功能

### 选项4: 无刷新令牌的JWT
- 优点:实现更简单
- 缺点:要么长命令牌(安全风险)要么频繁重新认证(不良用户体验)
- 为什么拒绝:刷新令牌提供更好的安全/用户体验平衡

## 实施注释
- 使用RS256(非对称)签名,允许在没有共享密钥的情况下进行验证
- 在数据库中存储哈希刷新令牌
- 实施令牌轮换
- 设置适当的CORS和安全标头
- 对Web客户端使用httpOnly cookies
- 在认证端点实施速率限制

## 参考
- [JWT最佳实践](https://tools.ietf.org/html/rfc8725)
- [OWASP认证备忘单](https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html)
- [Auth0博客关于刷新令牌](https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/)

## 注释
- 日期:2024-01-20
- 作者:John Doe
- 审核者:Jane Smith、安全团队
- 实施目标:Sprint 5
- 审核触发:安全审计或扩展超过50K用户

ADR存储和版本控制

存储选项

  1. 在仓库中(推荐)

    docs/adr/
    ├── 0001-use-postgresql.md
    ├── 0002-jwt-authentication.md
    ├── 0003-graphql-api.md
    └── README.md
    
  2. Wiki/Confluence

    • 便于发现
    • 可能与代码脱节
  3. 专用工具

    • log4brains
    • ADR Manager

版本控制

  • 使用顺序编号:00010002
  • 永不删除ADRs(标记为弃用/取代)
  • 链接相关ADRs
  • 与代码一起保存在版本控制中

链接ADRs

取代

# ADR-015: 使用REST API代替GraphQL

## 状态
已接受(2024-06-01)
取代:ADR-003

## 背景
在GraphQL(ADR-003)使用4个月后,我们遇到了问题:
- 查询复杂性导致性能问题
- 团队在GraphQL概念上挣扎
- 第三方开发人员请求REST
- 缓存复杂性超过了好处

## 决策
恢复到经过精心设计的REST API...

弃用

# ADR-003: 为公共API使用GraphQL

## 状态
已弃用(2024-06-01)
被取代:ADR-015

## 弃用原因
GraphQL的复杂性超过了我们用例的好处。
请参阅ADR-015了解新方法。

[原始内容保留在下面以供历史参考]

ADR审查流程

  1. 起草 - 作者创建ADR
  2. 审查 - 团队审查和评论
  3. 讨论 - 处理反馈
  4. 决策 - 接受、拒绝或请求更改
  5. 实施 - 执行决策
  6. 回顾 - 在3-6个月后审查结果

工具

adr-tools

# 安装
npm install -g adr-tools

# 初始化
adr init docs/adr

# 创建新的ADR
adr new "使用PostgreSQL作为主数据库"

# 列出ADRs
adr list

# 生成图表
adr generate graph

MADR(Markdown ADR)

# 安装
npm install -g madr

# 创建ADR
madr new "数据库选择"

log4brains

# 安装
npm install -g log4brains

# 初始化
log4brains init

# 预览
log4brains preview

# 构建静态网站
log4brains build

写作风格

做✅

  • 简洁明了
  • 使用主动语态
  • 包括日期和作者
  • 列出考虑的其他选项
  • 解释权衡
  • 必要时使用图表
  • 链接到参考

不要做❌

  • 写小说(保持在2页以内)
  • 使用没有解释的行话
  • 跳过"为什么"
  • 忽略其他选项
  • 将其作为规范(ADR≠规范)
  • 更新旧ADRs(相反,创建新的)

常见陷阱

  1. 太啰嗦 - 保持简洁
  2. 缺少上下文 - 总是解释为什么
  3. 没有其他选项 - 显示你考虑了选项
  4. 没有后果 - 列出利弊
  5. 太迟 - 在决策时编写,而不是实施后
  6. 太早 - 等到决策清晰
  7. 错误范围 - 不是每个决策都需要ADR

与文档集成

docs/
├── adr/              # 架构决策
├── api/              # API文档
├── guides/           # 如何指南
└── architecture/     # 架构图

交叉引用:

  • 从架构文档中链接ADRs
  • 在代码注释中引用ADRs
  • 在PR描述中包含ADR链接

团队采用策略

  1. 从小开始 - 只从主要决策开始
  2. 以身作则 - 架构师编写第一个ADRs
  3. 使其简单 - 提供模板和工具
  4. 一起审查 - 在团队会议中讨论ADRs
  5. 庆祝 - 认可好的ADRs
  6. 迭代 - 根据反馈改进流程

最佳实践

  1. 在决策时编写 - 而不是实施后
  2. 保持简短 - 最多1-2页
  3. 诚实 - 包括负面和风险
  4. 展示其他选项 - 证明你考虑了选项
  5. 使用模板 - 一致性有助于可读性
  6. 版本控制 - 与代码一起保存
  7. 定期审查 - 定期重新审视决策
  8. 链接相关ADRs - 显示决策演变
  9. 包括日期 - 上下文随时间变化
  10. 使其可搜索 - 使用清晰的标题和标签

资源