技术文档最难的不是"写得好",而是信息齐了没。内容散落的文档,写得再漂亮,半年后还是要重新啃代码。
这两年我把自己反复用的文档骨架整理成了 5 个模板。它们的共同点是:每一节都在回答一个具体的决策问题,而不是"把知道的都倒出来"。
判断标准:这份文档要不要写?
先给一条我认为最实用的标准:
如果一个决策,三个月后的你需要重新推演一遍才能理解,那就必须写下来。
按这个标准过滤,真正需要写的其实只有三类:
- 决策类:为什么选 A 不选 B(技术选型、架构方案)
- 约定类:大家必须一致遵守的东西(接口规范、命名规则、目录结构)
- 非显然的事实类:读代码看不出来的东西(历史包袱、踩过的坑、外部依赖的口头约定)
至于"这个函数做了什么"——代码本身就能说清楚,不需要文档。
模板一:设计文档(Design Doc)
用在动手之前。它的核心价值是让评审人能提问。
# [模块名] 设计文档
- 作者 / 日期 / 状态:[草稿 | 评审中 | 已定稿]
- 评审人:
## 1. 背景与问题
用 3 句话说清:现在的痛点是什么、影响了谁、不解决会怎样。
(不要写"为了提升系统性能"这种废话,要写"订单导出在 10 万行以上时超时,每周约 30 次工单")
## 2. 目标与非目标
### 目标
- 可量化的目标,如:导出 50 万行 P99 < 5s
### 非目标(同样重要)
- 本次不解决的历史订单迁移
- 不做实时导出
## 3. 方案设计
### 3.1 整体架构
(架构图 / 时序图)
### 3.2 关键流程
### 3.3 数据模型变更
### 3.4 接口定义
## 4. 备选方案与取舍
| 方案 | 优点 | 缺点 | 结论 |
| --- | --- | --- | --- |
| 方案 A | | | ✅ 采用 |
| 方案 B | | | ❌ 否决,原因:|
## 5. 影响面
- 对上游/下游接口的影响
- 对现有数据的影响(是否需要刷数据、回滚方案)
- 对运维的影响(新依赖、新监控项、新告警)
## 6. 风险评估
| 风险 | 概率 | 影响 | 应对 |
| --- | --- | --- | --- |
## 7. 上线计划
- 灰度策略 / 开关设计
- 回滚方案(必须写!)
- 验收标准
## 8. 待讨论问题
- [ ] xxx 需要和基础架构组确认
要点:第 4 节"备选方案与取舍"是设计文档的灵魂。没有备选方案的文档,本质是"通知"而不是"设计"。
模板二:故障复盘(Postmortem)
核心原则:对事不对人。一旦开始追责,后续的复盘都会失真。
# [YYYY-MM-DD] [故障标题] 复盘
- 影响时长:14:03 - 14:37(34 分钟)
- 影响范围:订单查询接口 100% 失败,约 1.2 万次请求
- 严重级别:P1
- 处理人:
## 1. 时间线(精确到分钟)
| 时间 | 事件 |
| --- | --- |
| 13:58 | 发布 v2.3.1,包含索引变更 |
| 14:03 | 监控告警:order-query P99 突增至 8s |
| 14:05 | 值班同学确认,开始排查 |
| ... | ... |
| 14:37 | 回滚完成,服务恢复 |
## 2. 根因(Root Cause)
分三层说:
- **直接原因**:新增索引的 DDL 在 2000 万行表上执行,锁表 30s
- **根本原因**:DDL 变更没有走 Online DDL 规范,且未在预发验证
- **系统性原因**:发布流程缺少"大表 DDL 审批"环节
## 3. 为什么没被提前发现
(这一节比根因更重要)
- 预发数据量只有线上的 1/100,无法暴露
- 监控只有 P99 告警,没有 DDL 执行时长指标
## 4. 改进项
| 改进项 | 类型 | 负责人 | 截止日期 |
| --- | --- | --- | --- |
| 大表 DDL 必须使用 gh-ost | 流程 | @xxx | 10-01 |
| 发布检查项加 DDL 审批 | 工具 | @xxx | 09-30 |
| 补 DDL 执行时长监控 | 监控 | @xxx | 09-25 |
## 5. 附录
- 相关日志、监控截图、变更单链接
要点:第 3 节"为什么没被提前发现"最容易被省略,但它才是防止同类故障复发的关键。
模板三:README
README 的读者是"第一次来到这个仓库的人",包括三个月后的你自己。
# 项目名
一句话说明这个项目是做什么的。
## 快速开始
```bash
# 三条命令以内跑起来,超过三条说明环境配置有问题
git clone xxx && cd xxx
make dev
open http://localhost:8080
```
## 环境要求
| 依赖 | 版本 | 说明 |
| --- | --- | --- |
## 常用命令
| 命令 | 作用 |
| --- | --- |
| `make test` | 跑单测 |
| `make lint` | 静态检查 |
| `make build` | 构建二进制 |
## 项目结构
(只写"为什么"这么分,不写"是什么")
- `internal/service`:业务编排,不直接访问 DB
- `internal/repo`:数据访问,禁止写业务判断
- `internal/adapter`:外部依赖适配,方便测试打桩
## 配置说明
关键配置项 + 默认值 + 是否必填。
## 常见问题
- 启动报 xxx:因为 aaa,执行 bbb 解决
## 相关文档
- 设计文档链接
- 部署文档链接
要点:README 里最该有的两样东西是"三条命令跑起来"和"项目结构为什么这么分"。
模板四:源码分析
用于读中间件、框架源码后的沉淀。关键是留下"结论"而不是"过程"。
# [组件名] 源码分析:[某个具体机制]
- 版本:v2.3.1(务必写明版本,源码分析脱离版本等于没写)
- 阅读入口:`xxx#yyy`
## 结论先行
用 5 句话以内说清这个机制的运作方式。
(半年后你只需要看这一段)
## 核心类图 / 时序图
## 关键流程拆解
### 步骤 1:xxx
代码位置:`a/b/C.java#L120`
做了什么 + **为什么这么做**
### 步骤 2:xxx
## 设计亮点
- 用 xxx 换取了 yyy(记住:任何设计都是取舍)
## 我踩过的坑
- 配置 xxx 时必须同时设 yyy,否则不生效
## 与其他实现的对比
要点:一定要有"结论先行"和"设计亮点"两节。前者省下未来的时间,后者才是真正的理解。
模板五:变更记录(CHANGELOG)
## [2.3.1] - 2026-06-05
### 新增
- 支持订单批量导出(上限 50 万行)
### 变更
- **不兼容变更**:`POST /api/order/query` 的 `status` 字段由 int 改为 string
- 迁移方式:见 [迁移指南](./migration.md)
### 修复
- 修复分页查询在最后一页返回空数组的问题(#1234)
### 安全
- 升级 log4j 至 2.17.2
要点:不兼容变更必须单独标注,并给出迁移方式。这是对使用者最基本的尊重。
一套通用的写作习惯
最后分享四个我一直在用的习惯:
- 先写结论,再写推导。 用"倒金字塔"结构:结论 → 依据 → 数据 → 附录。
- 每张图都要有 caption。 图单独发出去(比如在 IM 里转发)时,没有 caption 就是废图。
- 代码块必须标注语言。 否则渲染出来没有高亮,可读性差一大截。
- 写完通读一遍,删掉所有"其实""应该""大概"。 这类词会让文档失去可信度。
小结
| 文档类型 | 写在哪 | 核心章节 |
|---|---|---|
| 设计文档 | 动手前 | 备选方案与取舍 |
| 故障复盘 | 故障后 24h 内 | 为什么没被提前发现 |
| README | 首次提交时 | 快速开始 |
| 源码分析 | 读完源码后 | 结论先行 |
| 变更记录 | 每次发布 | 不兼容变更 |
文档不是写给领导看的,是写给未来那个已经忘记上下文的自己看的。按这个心态写,自然就写清楚了。