每个团队都在做决策,但大多数团队会忘记当初为什么这样决定。决策文档——不管你叫它ADR、RFC还是决策日志——通过记录重要选择的背景、备选方案和推理过程来解决这个问题。
本指南涵盖了何时该写决策文档、如何让它们真正发挥作用,以及可以直接拿来用的模板。
为什么决策文档很重要#
1. 清晰度和透明度#
决策文档提供了一种结构化的方式来阐明关键决策、考虑过的方案以及最终选择背后的理由。通过在统一的文档中记录这些信息,团队可以减少误解,让所有人保持一致。透明度能促进利益相关者之间的信任,因为它表明决策是经过深思熟虑、广泛参与后做出的。
2. 跨团队协调#
大型项目通常涉及具有不同视角和优先级的跨职能团队。决策文档通过提供评估方案的清晰框架来帮助协调这些不同的群体。当各方都理解影响决策的因素时,更容易获得认同并齐心协力向前推进。
3. 避免返工和无休止的争论#
你的团队重复讨论过多少次同样的问题?“为什么当初选了PostgreSQL而不是MongoDB?““API版本策略不是已经定了吗?“文档化决策能帮助团队避免反复讨论已经解决的问题。有了过去决策及其背景的清晰记录,团队可以专注于执行而不是重走老路。
4. 保存组织知识#
项目可能持续数月甚至数年,期间团队成员会有进出。决策文档作为历史记录,保存了为什么选择某条路径的关键知识——不只是做了什么决定。这确保了即使人员变动也能保持连续性。
5. 问责制和所有权#
通过明确决策者、贡献者和审批者,决策文档创造了问责制。每个人都知道自己的角色和责任,这让跟踪进度和确保执行变得更加容易。
什么时候该写决策文档#
不是每个决策都需要正式文档。以下情况适合使用决策文档:
影响大的决策:
- 技术栈选择(框架、数据库、云服务商)
- 架构模式(微服务 vs 单体、事件驱动 vs 请求-响应)
- 第三方供应商选型
- 安全和合规方案
有长期影响的决策:
- 外部系统依赖的API契约
- 影响多个服务的数据模型设计
- 对已有模式的破坏性变更
需要跨团队对齐的决策:
- 影响多个团队或部门的变更
- 需要非技术利益相关者认同的决策
- 资源分配和优先级选择
从Spike中产生的决策:
- 当一个限时调研结束后,把发现和最终决策记录到正式文档中
可以跳过正式文档的情况:
- 容易撤销的决策
- 团队内部的实现细节
- 对直接范围外影响很小的选择
决策文档的生命周期#
了解决策文档在组织中的流转过程有助于最大化其效果:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 草稿 │ ──► │ 评审 │ ──► │ 批准 │ ──► │ 实施 │
│ 提议 │ │ 反馈 │ │ 接受 │ │ 完成 │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
│
▼
┌─────────────┐
│ 否决/ │
│ 取代 │
└─────────────┘草稿/提议: 作者创建初始文档,概述问题和潜在方案。
评审/反馈: 利益相关者审查文档、提出问题、建议替代方案。这个阶段至关重要——假设会被挑战,盲点会被发现。
批准/接受: 决策者(通常是技术负责人、架构师或产品负责人)做出最终决定。
实施/完成: 团队执行决策。更新文档以反映实施过程中的任何变化。
否决/取代: 有些决策在评审中被否决,有些随着情况变化而过时。明确标记这些状态以避免混淆。
如何写出有效的决策文档#
从问题开始,而不是方案#
一个常见的错误是直接跳到方案。相反,先明确说明:
- 我们要解决什么问题?
- 为什么这个问题现在很重要?
- 如果不解决会怎样?
这种框架确保在讨论方案之前,所有人先对问题达成共识。
展示多个选项#
即使你有强烈的偏好,也要记录至少2-3个替代方案。这体现了尽职调查,也帮助其他人理解为什么推荐的方案更好。每个选项应包含:
- 简要描述
- 优缺点
- 工作量估算(粗略的T恤尺码就行:S/M/L/XL)
- 风险和缓解策略
明确说明取舍#
每个决策都涉及取舍。坦率地承认它们:
- “我们选择了上市速度而不是架构纯洁性”
- “我们接受了更高的运维复杂度以换取更好的可扩展性”
- “我们优先考虑开发者体验而不是原始性能”
这些声明帮助未来的读者理解当时的背景和约束。
包含具体示例#
抽象的讨论导致抽象的决策。用以下内容让文档更具体:
- 展示方案如何实现的代码片段
- 说明系统交互的图表
- 指向Spike中概念验证实现的链接
设定明确的时间线#
没有截止日期的决策会无限期拖延。明确指定:
- 什么时候必须做出决策
- 谁需要在什么时候之前提供意见
- 如果决策延迟,什么会阻碍进度
示例:架构决策记录(ADR)#
下面是一个实际的决策文档示例:
# ADR-001: API Authentication Strategy
**Status:** Accepted
**Date:** 2024-11-15
**Decision Maker:** Sarah Chen (Platform Architect)
**Contributors:** Backend Team, Security Team, Mobile Team
## Context
Our public API currently uses API keys for authentication. As we expand to
support third-party integrations and mobile apps, we need a more robust
authentication mechanism that supports:
- Token expiration and refresh
- Scoped permissions
- User-level authentication (not just service-level)
## Decision
We will implement OAuth 2.0 with JWT tokens for API authentication.
## Options Considered
### Option 1: OAuth 2.0 with JWT (Recommended)
**Description:** Industry-standard protocol with self-contained tokens
| Pros | Cons |
|------|------|
| Industry standard, well-documented | More complex initial implementation |
| Self-contained tokens reduce database lookups | Tokens cannot be revoked instantly |
| Broad library support | Requires refresh token management |
**Effort:** Medium (2-3 sprints)
### Option 2: Session-based Authentication
**Description:** Traditional server-side sessions with cookies
| Pros | Cons |
|------|------|
| Simple to implement | Not suitable for mobile apps |
| Easy to revoke sessions | Requires sticky sessions or shared session store |
| Familiar to most developers | Doesn't scale as well |
**Effort:** Small (1 sprint)
### Option 3: Custom Token System
**Description:** Build our own token-based authentication
| Pros | Cons |
|------|------|
| Fully customizable | Reinventing the wheel |
| No external dependencies | Security risks from custom implementation |
**Effort:** Large (4+ sprints)
## Consequences
- Mobile team can implement standard OAuth flows
- We'll need to set up a token refresh mechanism
- API documentation will need updates for OAuth flows
- Existing API key users will need a migration path (6-month deprecation)
## References
- [OAuth 2.0 RFC 6749](https://tools.ietf.org/html/rfc6749)
- Internal spike document: [Authentication Options Spike](/spikes/auth-spike-2024)
- Security team review: SEC-2024-042决策文档模板#
把这个模板作为你团队的起点:
| 属性 | 详情 |
|---|---|
| 决策/议题名称 | [清晰、描述性的标题] |
| 状态 | [草稿 / 评审中 / 已批准 / 已否决 / 已取代] |
| 影响度 | [高 / 中 / 低] |
| 负责人 | [推动决策的人] |
| 决策者 | [拥有最终决定权的人] |
| 截止日期 | [做出决策的截止时间] |
问题描述#
我们要解决什么问题?为什么重要?不采取行动的代价是什么?
背景#
提供上下文、历史和相关细节。链接到相关文档、过去的决策或Spike结果。
约束条件#
所有方案必须遵守哪些限制或要求?
- 预算约束
- 时间线要求
- 技术约束(现有系统、技能储备)
- 合规或安全要求
考虑的方案#
| 选项 | 描述 | 优点 | 缺点 | 工作量 | 风险 |
|---|---|---|---|---|---|
| 选项 1 | 描述 | 优点A,优点B | 缺点A,缺点B | S/M/L/XL | 低/中/高 |
| 选项 2 | 描述 | 优点A,优点B | 缺点A,缺点B | S/M/L/XL | 低/中/高 |
| 选项 3 | 描述 | 优点A,优点B | 缺点A,缺点B | S/M/L/XL | 低/中/高 |
建议#
说明推荐的选项,并总结在约束和取舍条件下为什么它是最佳选择。
决定#
做出决定后记录最终决策。如果与建议不同,说明原因。
后果#
这个决策有什么影响?需要哪些后续工作?
行动项#
| 行动 | 负责人 | 截止日期 |
|---|---|---|
| 行动 1 | 姓名 | 日期 |
| 行动 2 | 姓名 | 日期 |
参考资料#
相关文档、外部资源、Spike结果或过去决策的链接。
建设决策文档文化的建议#
让文档容易被找到: 把决策文档存放在固定的位置——仓库中的专用文件夹、Wiki空间或文档系统。找不到就不会有人用。
保持轻量: 写起来要花好几天的决策文档不会有人写。从必要内容开始,在重要的地方补充细节。
在回顾会上复盘: 在回顾会中定期审视过去的决策。哪些决策效果好?哪些你会换个做法?这个反馈循环能改善未来的决策质量。
把决策和实现关联起来: 在代码注释、提交信息和Pull Request中引用决策文档。这在"为什么"和"做了什么"之间建立了可追溯性。
版本化和更新: 情况会变。当一个决策被取代时,不要删除旧文档——标记为已取代并链接到新决策。这样历史记录就保留下来了。
常见陷阱#
分析瘫痪: 别让追求完美成为好的敌人。设定截止日期,用现有信息做出最好的决定,然后继续推进。大多数决策在需要时都可以重新审视。
走过场式审批: 如果评审总是没有实质性反馈就通过了,那这个流程就没有在创造价值。鼓励真正的批评和不同视角。
孤儿文档: 写了但从不执行的决策文档比没有文档还糟糕。确保决策能转化为行动。
过多细节: 决策文档应该解释"为什么"和"是什么”,而不是"怎么做”。实现细节应该放在技术规格书里,不是决策记录里。
忽视异议: 如果利益相关者不同意某个决策,记录他们的顾虑。即使决策不变,承认异议是一种尊重,也为未来的读者提供了重要的背景。
总结#
决策文档不是额外负担——它们是在省时间。每一次"我们当初为什么选了X?“的对话因为有人能直接去读ADR而没有发生,就是实实在在节省下来的时间。
从你的下一个重要决策开始吧。写下来,哪怕写得粗糙也没关系。边做边完善流程。随着时间推移,你会积累起一份可搜索的记录,让未来的决策更快、新人上手更容易。
延伸阅读#
本站相关文章:
- 理解敏捷软件开发中的Spike - 在做重大决策前用Spike来收集信息
- 回顾会:反馈循环的力量 - 作为持续改进流程的一部分审视过去的决策
- API规划 - 将决策文档原则应用于API设计
- 用GitHub做项目管理 - 在代码旁边存储和跟踪决策文档
外部资源:
- Documenting Architecture Decisions - Michael Nygard - 让ADR流行起来的那篇经典博文
- ADR GitHub Organization - 架构决策记录的工具、模板和示例
- Design Docs at Google - Google的设计文档实践
- RFC Process at Rust - Rust编程语言完善的RFC流程
- Spotify’s Decision Record Template - Spotify的架构决策记录实践

