工程复盘模板:从事实、判断到改进行动
复盘要基于事实
上个迭代结束后,团队开了一次常规复盘。半小时过去,发言集中在「这次需求变太多了」「测试时间不够」「跨组协调太慢」。我翻了下记录,全是感受,没有一条带数据。
这不是复盘,是吐槽大会。
工程复盘最容易掉进的坑,就是大家凭印象讨论、凭情绪下结论。谁声音大谁主导,最后产出五六条「加强沟通」「提高质量」之类的空话,写进文档再没人看。
Google SRE 书里有一句话我印象很深:「Postmortems are letters you write to future team members.」复盘文档是写给未来团队成员看的,不是写给今天参会的人看的。这意味着它必须自包含、有数据、可追溯,而不是几个模糊的形容词。
这篇文章把复盘拆成一个可操作的模板。从收集事实开始,经过根因分析,最后落到可跟踪的改进行动。每个环节我都会给出具体做法和容易踩的坑。
理论框架:复盘不是总结,是结构化的事后分析
Postmortem 和 Retrospective 的区别
很多团队把事故复盘(Postmortem)和迭代回顾(Retrospective)混为一谈。两者目标不同,颗粒度也不同。
| 维度 | Postmortem(事故复盘) | Retrospective(迭代回顾) |
|---|---|---|
| 触发条件 | 具体事故或线上故障 | 固定节奏(每迭代/每月) |
| 关注范围 | 单一事件的根因和影响 | 一段时间内的整体改进 |
| 参与角色 | 直接相关人 + SRE | 全团队 |
| 输出物 | RCA 报告 + 修复 Action | 改进行动列表 |
| 时间压力 | 事故发生后 48-72 小时内 | 按迭代节奏 |
这篇文章的模板偏 Postmortem,但框架同样适用于 Retrospective——只是后者通常不需要深入到日志级的事实梳理。
5 Whys 不是万能药
5 Whys 是丰田生产系统里发展出来的根因分析方法。美团技术团队在实践中总结了一套流程:先用七何模型(What / Who / Where / When / How / Why / How much)记录事件全貌,再连续追问为什么,串联因果链条,直到找到制度层面的根因。
但 5 Whys 有一个常见误用:只沿着一条因果链往下追,忽略了并行原因。实际工程中,一个问题往往是多个因素叠加的结果。这时候需要结合鱼骨图的思路,从人、流程、工具、环境四个维度分别追问。
无责文化是前提
Atlassian 的 Postmortem 指南强调一点:blameless 不等于 no accountability。不追责的意思是,讨论焦点放在「系统为什么允许这个错误发生」,而不是「谁犯了这个错」。
这不是政治正确。Google SRE 的实践证明,一旦复盘变成追责大会,工程师就会隐藏信息,复盘质量直接归零。你得到的不是真相,而是一个经过自我保护过滤后的版本。
PagerDuty 也指出:「A successful postmortem process is based on a culture of honesty, learning, and accountability. Culture change requires management buy-in.」管理层的支持是复盘文化落地的必要条件。
模板结构:八个模块逐一展开
一份完整的复盘文档,我通常按以下结构组织:
模块一:事实收集
在讨论任何原因之前,先把事实铺开。事实包括:
- 时间线:从第一个异常信号到问题完全恢复,按时间排序。精确到分钟。
- 影响范围:多少用户受影响?哪些功能不可用?持续多久?
- 检测方式:是告警发现的,还是用户反馈的?
- 关键数据:错误率、延迟、QPS、错误日志片段。
这一步的关键是:只记录,不解释。任何带有判断的描述(「因为 XX 没做好」)都要删掉。
# 好做法:纯事实描述
timeline:
- time: "2026-06-20T14:32:00+08:00"
event: "监控告警:订单服务 P99 延迟超过 3s"
- time: "2026-06-20T14:35:00+08:00"
event: "值班人员开始排查,发现数据库连接池耗尽"
- time: "2026-06-20T14:52:00+08:00"
event: "扩容连接池后恢复,持续 20 分钟"
# 坏做法:混入主观判断
timeline:
- time: "2026-06-20T14:32:00+08:00"
event: "由于数据库配置不当,导致系统变慢"
# ❌ "配置不当"是结论,不是事实。事实阶段不应出现归因模块二:做得好的地方
复盘不只找问题。识别有效做法同样重要——否则你会在修复问题的同时,不小心改掉那些有用的实践。
# 好做法:具体描述有效实践
## 做得好的地方
1. 告警在 3 分钟内触达值班人员,MTTD(平均检测时间)符合 SLA
2. 值班人员按 Runbook 执行了连接池扩容操作,未做临时 patch
3. 恢复后立即建立了事后分析文档,没有拖过 48 小时
# 坏做法:笼统表扬
## 做得好的地方
1. 大家反应很快
2. 团队协作不错
# ❌ 没有具体行为,下次无法复现模块三:问题与根因分析
这一步是复盘的核心。把每个问题单独拿出来,用 5 Whys 追到系统层面的根因。
# 问题:数据库连接池耗尽
## 5 Whys 分析
1. **为什么连接池耗尽?**
→ 慢查询占用了大量连接,释放速度跟不上请求速度
2. **为什么有慢查询?**
→ 新上线的报表查询缺少索引,全表扫描
3. **为什么缺少索引的查询能上线?**
→ SQL Review 流程跳过了这个查询,因为它是通过 ORM 动态生成的
4. **为什么 ORM 生成的查询不经过 SQL Review?**
→ Review 流程只覆盖手写 SQL,未覆盖 ORM 查询
5. **为什么流程存在这个盲区?**
→ 半年前引入新 ORM 框架时,没有同步更新 Review 规则
## 根因
SQL Review 流程未覆盖 ORM 动态生成的查询,导致缺少索引的慢查询进入生产环境。根因分析要回答三个问题:问题为什么发生?为什么没有被提前拦截?为什么没有系统性防范?
# 好做法:三维度追问
1. 因何爆发?—— 直接技术原因
2. 因何未拦截?—— 检测/预防机制的缺口
3. 因何无系统性防范?—— 制度/流程/工具层面的缺失
# 坏做法:停在技术修复
1. 因何爆发?—— 慢查询
2. 解决方案:加索引
# ❌ 加了索引还会有下一个慢查询。问题在于流程缺口,不在单条 SQL模块四:指标变化
用数据说话。对比事故发生前后的关键指标。
| 指标 | 事故前 | 事故期间 | 事故后 | 数据来源 |
|---|---|---|---|---|
| P99 延迟 | 200ms | 4500ms | 180ms | APM |
| 错误率 | 0.1% | 12.3% | 0.08% | 日志聚合 |
| 订单成功率 | 99.5% | 67.2% | 99.6% | 业务监控 |
| 数据库连接使用率 | 35% | 100% | 42% | 数据库监控 |
没有指标的复盘,说服力减半。如果团队还没有建设基础监控,这件事本身就应该成为一条 Action。
模块五:复盘流程图
把复盘过程标准化,可以减少遗漏。下面是我实际使用的复盘流程:
关键节点:每个 Action 必须满足三个条件——有明确的 Owner、有 Deadline、能在任务系统中被追踪。否则这条 Action 就不会被执行。
模块六:改进行动
这是复盘最容易烂尾的地方。十几条 Action 列出来,三个月后回头看,一条都没做。
经验是:每次复盘最多定 3 条 Action。选影响最大、投入最合理的那几条。做完再回来加。
# 好做法:具体、可追踪
actions:
- id: ACTION-001
description: "在 SQL Review 规则中增加 ORM 动态查询的扫描配置"
owner: "@zhangsan"
priority: P0
deadline: "2026-07-05"
tracker: PROJ-1234
verification: "CI 中 ORM 生成的 SQL 能触发慢查询检测"
status: in_progress
- id: ACTION-002
description: "为报表查询添加复合索引 (tenant_id, created_at)"
owner: "@lisi"
priority: P0
deadline: "2026-06-28"
tracker: PROJ-1235
verification: "报表查询 P95 < 500ms"
status: done
# 坏做法:模糊、无人跟
actions:
- "优化数据库性能"
- "加强代码审查"
- "完善监控告警"
# ❌ 没有 Owner、没有 Deadline、没有验收标准,永远不会被执行模块七:行动优先级排序
不是所有改进都同等重要。用「影响 × 投入」矩阵来排序:
| 象限 | 影响大 + 投入小 | 影响大 + 投入大 | 影响小 + 投入小 | 影响小 + 投入大 |
|---|---|---|---|---|
| 策略 | 立即执行 | 排入下个迭代 | 顺手做了 | 暂不处理,记录在案 |
| 举例 | 补 SQL Review 规则 | 引入查询分析引擎 | 加一条告警规则 | 重构 ORM 层 |
模块八:闭环检查
复盘文档写完后,设一个 30 天后的检查点。检查内容包括:
- Action 是否全部完成?
- 未完成的 Action 原因是什么?是优先级变了,还是被阻塞了?
- 已完成的 Action 是否有效?指标是否有改善?
- 是否需要新增 Action?
# 30 天后检查模板
review_at: "2026-07-25"
actions_status:
- id: ACTION-001
status: done
effect: "SQL Review 已覆盖 ORM 查询,拦截了 3 条潜在慢查询"
- id: ACTION-002
status: done
effect: "报表查询 P95 从 2.1s 降到 320ms"
- id: ACTION-003
status: blocked
reason: "依赖基础设施团队提供查询分析引擎,已排入 Q3"实战案例
案例一:接口字段变更导致客户端崩溃
这是美团技术团队分享过的一个真实场景,我做了适当改编。
场景:iOS 客户端在发版后大面积崩溃,crash 日志指向一个 JSON 解析异常。
问题排查过程:
| 步骤 | 做了什么 | 发现了什么 |
|---|---|---|
| 1 | 查看 crash 堆栈 | NSNull 被当作 String 解析 |
| 2 | 对比 API 响应变化 | 后端新增了 3 个可选字段,部分用户返回 null |
| 3 | 检查 API 变更流程 | 后端只通知了 Android 团队,iOS 团队未收到 |
| 4 | 追问为什么 iOS 没收到 | 没有强制的跨端 API 变更通知机制 |
| 5 | 追问为什么客户端能崩 | iOS 端没有对可选字段做 nil 兜底 |
根因:
- API 变更没有强制跨端同步流程
- 客户端代码对可选字段缺少防御性处理
- 团队在质量上存在盲目自信,没有代码审查制度
修复方案:
// 坏做法:直接强转,不处理 null
let username = response["username"] as! String
// ❌ 如果 username 为 null 或类型不对,直接 crash
// 好做法:防御性解析
struct UserResponse: Codable {
let username: String?
let email: String?
let nickname: String?
enum CodingKeys: String, CodingKey {
case username, email, nickname
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
// 所有可选字段都提供默认值
self.username = try container.decodeIfPresent(String.self, forKey: .username) ?? ""
self.email = try container.decodeIfPresent(String.self, forKey: .email) ?? ""
self.nickname = try container.decodeIfPresent(String.self, forKey: .nickname) ?? "用户"
}
}
// ✅ 即使后端返回 null,客户端也不会崩溃制度层面的改进:
actions:
- "建立 API 变更的跨端通知机制:后端修改接口必须在跨端周会同步"
- "客户端所有 API 响应字段必须使用 Codable + decodeIfPresent"
- "新增 API 变更 Checklist,包含:向前兼容 / 可选字段 / 跨端通知"案例二:数据库连接池耗尽
场景:某次发版后,订单服务 P99 延迟从 200ms 飙升到 4500ms,持续约 20 分钟。
时间线:
14:30 新版本上线
14:32 监控告警:订单服务 P99 > 3s
14:35 值班人员开始排查
14:38 发现数据库连接池使用率 100%
14:42 定位到一条全表扫描的慢查询
14:48 临时方案:扩容连接池 + 限流报表服务
14:52 P99 恢复到正常水平
5 Whys 分析:
1. 为什么 P99 飙升?
→ 数据库连接池被耗尽
2. 为什么连接池耗尽?
→ 新上线的报表查询没有索引,每次执行 3-5 秒,占满连接
3. 为什么没有索引的查询能上线?
→ 这条 SQL 是 ORM 动态生成的,绕过了 SQL Review
4. 为什么 ORM 生成的 SQL 不需要 Review?
→ Review 规则只覆盖了手写 SQL 文件
5. 为什么规则有这个盲区?
→ 半年前引入新 ORM 框架时,没有同步更新 Review 流程
修复方案对比:
-- 坏做法:只加索引,不改流程
CREATE INDEX idx_report_tenant_created
ON orders (tenant_id, created_at);
-- ❌ 解决了当前慢查询,但下一条 ORM 生成的慢查询还会出问题
-- 好做法:加索引 + 在 CI 中检测 ORM 慢查询
CREATE INDEX idx_report_tenant_created
ON orders (tenant_id, created_at);
-- 同时在 CI 管道中增加 ORM SQL 分析步骤
-- ci/sql-review.yml
sql_review:
sources:
- type: manual # 手写 SQL
path: "db/migrations/"
- type: orm # ORM 生成的 SQL
config: "config/orm-review.yml"
rules:
- max_execution_time: 500ms
- require_index: true
- reject_full_table_scan: true案例三:复盘会议跑偏
场景:一次迭代回顾,30 分钟的会议,20 分钟在讨论一个需求评审的细节。最后 10 分钟草草列了 8 条 Action,没人认领。
问题分析:
| 问题 | 原因 | 修复 |
|---|---|---|
| 讨论跑偏 | 没有 Time Keeper,没有议程 | 会前发议程,每个议题限时 |
| Action 太多 | 试图一次解决所有问题 | 最多 3 条,选投入产出比最高的 |
| 没人认领 | 没有提前分配 | 会前初步指定 Owner,会上确认 |
| 没有跟进 | 没有录入跟踪系统 | 会后当天录入 Jira/Linear |
复盘会议的标准流程:
meeting_agenda:
- item: "回顾上次 Action 完成情况"
time: "5min"
owner: "Scrum Master"
- item: "快速过数据:本迭代交付量、Bug 数、关键指标"
time: "5min"
owner: "Tech Lead"
- item: "做得好的(每人 1-2 条)"
time: "5min"
owner: "全员"
- item: "待改进的(每人 1-2 条)"
time: "5min"
owner: "全员"
- item: "选出 Top 3 改进项,指定 Owner 和 Deadline"
time: "10min"
owner: "Tech Lead 主持"常见反模式与修正
复盘这件事,做多了会发现反模式就那几种。整理成表,方便对照:
| 反模式 | 表现 | 后果 | 修正方法 |
|---|---|---|---|
| 情绪发泄会 | 「XX 部门太不靠谱了」 | 团队对立,没有产出 | 回到事实,要求给出数据和具体时间线 |
| 泛泛总结 | 「要加强沟通」「要提高质量」 | 写了等于没写 | 每条 Action 必须可验证,有 Owner |
| 追责大会 | 「这个问题是 XXX 造成的」 | 没人敢说真话 | 主持人明确开场:对事不对人 |
| Action 堆积 | 一次列出 15 条改进 | 一条都做不完 | 最多 3 条,选影响最大的 |
| 复盘拖延 | 事故两周后才开 | 细节模糊,变成猜测 | 48-72 小时内完成,趁记忆新鲜 |
| 只有问题没有亮点 | 全程在批评 | 团队士气下降 | 强制先说做得好的,再说待改进的 |
| 复盘无闭环 | 上次 Action 从不检查 | 同样的问题反复出现 | 每次复盘先回顾上次 Action |
复盘检查清单
按阶段分组,每次复盘可以对照勾选:
会前准备
- 事故发生后 48-72 小时内启动复盘
- 收集完整的时间线,精确到分钟
- 整理关键指标数据(错误率、延迟、影响用户数)
- 准备会议议程,每个议题分配时间
- 邀请直接相关人,避免无关人员
会中讨论
- 主持人开场声明:对事不对人,无责复盘
- 先过事实和数据,再讨论原因
- 先说做得好的,再说待改进的
- 每个问题做 5 Whys 分析,追到系统层面
- 根因覆盖三个维度:技术原因 + 检测缺口 + 制度缺失
- 改进 Action 不超过 3 条
Action 落地
- 每条 Action 有明确的 Owner(一个人,不是团队)
- 每条 Action 有 Deadline
- 每条 Action 录入任务跟踪系统(Jira / Linear / 飞书)
- 每条 Action 有可验证的完成标准
闭环检查
- 30 天后检查 Action 完成情况
- 已完成的 Action 评估效果(指标是否有改善)
- 未完成的 Action 说明原因并决定继续还是放弃
- 复盘文档归档,团队可见
参考资料
- Google SRE Workbook - Postmortem Culture — Google 的 Postmortem 文化指南,涵盖无责原则、文档结构和 Action 跟踪。
- Atlassian - How to Run a Blameless Postmortem — 无责复盘的操作指南,强调心理安全。
- PagerDuty - The Blameless Postmortem — 从 PagerDuty 的视角看复盘文化落地。
- 美团技术博客 - 5 Whys 分析法在美团工程师中的实践 — 5 Whys 在国内工程团队的落地经验。
- IBM - 什么是根因分析(RCA) — RCA 方法论的系统性介绍。
- Atlassian - 根本原因分析详解 — 基于事实依据的结构化 RCA 流程。
- 好斗的鱼 - SRE 故障复盘流程实践指南(2026) — 最新的中文 SRE 复盘实践。