故障复盘怎么写——不追责、找根因、防重犯的完整方法论
故障复盘怎么写——不追责、找根因、防重犯的完整方法论
适读人群:经历过或即将参与故障复盘的工程师和技术管理者 | 阅读时长:约17分钟 | 核心价值:一套真正有效的故障复盘方法,让复盘不再是走形式的认罪书
我见过两种故障复盘。
第一种:故障复盘文档写完了,开会,大家围着讨论"到底是谁的锅",最后某人背了个责任,文档归档,什么也没改变,一个月后同样的故障再次发生。
第二种:故障复盘文档写完了,开会,大家梳理了完整的事件时间线,找出了4个根因,列了7条改进措施,每条都有负责人和 deadline,3个月后这类故障从未再出现。
两种复盘的区别不在于"态度够不够认真",而在于方法论的正确性。
今天我想把第二种复盘的方法论完整讲清楚。
故障复盘最大的误区:把它变成追责会
追责是最低效的复盘方式,原因有三个:
第一,追责不能防止故障再次发生。 就算你找到了"犯错的人"并且惩罚了他,导致故障的那个系统漏洞还在,下次换另一个人犯同样的错,故障还会发生。
第二,追责破坏心理安全感。 如果工程师们知道出了故障会被追责,他们会在工作中变得保守,不敢做有风险的改进,不愿意坦诚地报告问题。一个没有心理安全感的团队,无法有效地学习和改进。
第三,追责误导了问题的焦点。 故障几乎从来不是"一个人犯了一个错"那么简单,背后通常有系统性的原因:流程的缺失、工具的不完善、监控的盲区、设计的缺陷。把注意力放在"谁的错"上,会掩盖这些更本质的问题。
Etsy、Google 等工程文化先进的公司,都把"无责化复盘"(Blameless Postmortem)作为核心工程文化之一。无责化不是说没有人对故障负责,而是说把精力放在"系统怎么改进",而不是"人怎么被惩罚"。
故障复盘文档的结构
一份完整的故障复盘文档应该包含:
1. 基本信息
- 故障编号、日期
- 严重程度(P0/P1/P2)
- 涉及系统
- 处理负责人
2. 影响摘要(2-3句话的简短总结)
- 影响了什么?
- 持续多长时间?
- 受影响的用户/请求数量是多少?
3. 事件时间线(最重要的部分之一)
按时间顺序,记录从故障发生到完全恢复的每一个关键事件,精确到分钟级。
格式示例:
14:02 告警触发:支付成功率下降告警,当前成功率82%
14:04 老张收到告警,开始排查
14:08 确认问题在支付网关层,发现午后部署的版本有异常
14:11 开始回滚操作
14:17 回滚完成,支付成功率恢复至99.1%
14:21 观察稳定,确认故障恢复
14:45 告警恢复通知发出时间线的价值不只是记录"发生了什么",更重要的是记录"发现-响应-恢复"各个阶段花了多少时间,这些数字会指向改进的方向。
4. 根因分析
这是复盘最核心、也最难写好的部分。
根因分析的目标是找到"如果这件事没有发生,故障就不会发生"的那个最根本的原因。
常用的方法是"5 Why"分析法:
故障现象:支付成功率下降
- 为什么?→ 支付网关频繁返回超时
- 为什么超时?→ 数据库查询耗时显著增加
- 为什么查询变慢?→ 午后发版加了一个没有索引的查询
- 为什么新查询没有索引?→ 开发测试用的是小数据量,没有发现性能问题
- 为什么测试环境没有代表性的数据量?→ 测试环境的数据量是生产的1%,且没有性能压测流程
这样挖到第5层,根因已经不是"某个工程师写了慢查询",而是"测试环境和生产环境的差距 + 没有性能压测流程"。这两个问题,才是值得投入资源去解决的系统性问题。
注意:5 Why 要到"系统/流程"层面才算找到根因,停留在"人为错误"层面的根因是不完整的。
5. 贡献因素
除了根因,还要列出所有"使故障变得更严重或者更难发现"的因素,叫贡献因素。
比如:
- 监控告警的延迟(从故障发生到告警触发,晚了4分钟)
- 回滚操作需要人工执行,没有自动化工具(导致回滚花了6分钟而不是2分钟)
- 技术交接不完整(值班的人不熟悉这个系统的架构,找人问花了5分钟)
这些贡献因素都是改进的方向。
6. 改进措施
改进措施要满足几个条件:
可执行:不是"要加强代码审查"这种模糊的宣言,而是"在每个 PR 合并前增加数据库慢查询检测步骤,由XX在Y月Z日之前完成工具配置"。
有负责人:每条改进措施要指定具体的负责人,不是"团队",而是"老王"。
有 deadline:每条措施要有完成时间节点。
有优先级:把改进措施分类:高优(必须在下次发版前完成)、中优(本季度内完成)、低优(下季度考虑)。
7. 经验教训
用一两句话总结这次故障给团队的最重要教训。这部分要简短,但要有洞见。
我踩过的坑:复盘文档写完了但没有用
有一段时间,我们团队故障复盘的质量其实还可以,文档写得很完整,根因找得也准,改进措施也列了一堆。但一个季度过去,发现同类故障还在重复发生。
后来我发现问题出在哪里:改进措施没有跟进机制。
改进措施列了之后,没有人定期检查这些措施是否真的被执行了。负责人有时候忘了,有时候被其他事情冲掉了,有时候觉得"反正也没人查"就拖延了。
解决方法是建立改进措施的跟进机制:
- 把所有改进措施录入到 Jira/Linear/飞书等任务工具中,形成正式的工单
- 每周的工程例会,花5分钟过一遍未完成的改进措施
- 下一次发生同类故障时,先检查上次复盘的改进措施是否完成
只有被执行的改进措施,才有价值。写在文档里锁起来的改进措施,等于没有。
如何让复盘会开得有建设性
复盘文档是基础,但复盘会才是让整个团队共同从故障中学习的关键环节。
会议开始时,主持人要明确设定基调:
- 这次会议的目的是学习和改进,不是追责
- 鼓励每个人坦诚分享,包括"我当时为什么做了XX这个决定"
- 所有发言都是为了帮助我们找到更好的解决方案,没有"蠢问题"
在讨论根因时,要警惕一种常见的陷阱:事后诸葛亮偏见。
当我们知道事情结果之后,再回看决策过程,很容易觉得"当时的决策者怎么这么蠢,这么明显的问题都看不出来"。
但当时的决策者,是在信息不完整、时间压力大、认知负担很重的情况下做出决策的。我们在复盘时要站在当时的信息和约束条件下,去理解那个决策,而不是用"上帝视角"批评它。
"在当时的条件下,他做了一个合理的决策,只是这个决策基于一个错误的假设,为什么那个假设是合理的?如何避免下次又有人基于同样的错误假设做决策?"
这才是复盘应该有的追问方式。
一个关于复盘文化的更大视角
我认为,一个团队的故障复盘质量,很大程度上反映了这个团队的工程文化成熟度。
如果工程师们都不愿意写复盘,或者复盘写得都是敷衍,往往意味着:团队里有追责氛围,写复盘会让自己陷入麻烦。
如果工程师们愿意把故障事件详细记录下来,坦诚地承认决策失误,积极地提出改进措施,这意味着:团队有心理安全感,大家相信坦诚会带来成长而不是惩罚。
作为技术管理者,我做过的最有价值的一件事,是在一次复盘会上,当众承认自己对某个架构设计的判断失误,这个失误是那次故障的贡献因素之一。
这件事之后,团队的复盘质量肉眼可见地提高了,因为大家看到了:坦诚不会让你被批评,只会让大家一起找到更好的方案。
好的故障复盘 vs 坏的故障复盘:对比
让我用一个具体的例子,展示"好的复盘"和"坏的复盘"的区别。
同一个故障场景: 支付服务因为数据库连接池耗尽,导致15分钟内支付不可用。
坏的复盘结论:
根本原因:工程师小张在代码里使用了一个低效的查询,导致每次请求占用数据库连接时间过长,最终耗尽连接池。 改进措施:小张需要在未来的代码中更加注意数据库性能。
好的复盘结论:
直接原因:数据库连接池耗尽,新请求无法获取连接。
根本原因链:
- 为什么连接池耗尽?→ 某个接口的数据库查询平均耗时从正常的5ms上升到了800ms
- 为什么查询变慢?→ 上午的发版引入了一个缺少索引的查询,在数据量大的情况下走了全表扫描
- 为什么缺少索引的查询没有被发现?→ 测试环境的数据量只有生产的0.1%,全表扫描在测试环境下耗时可以忽略不计
- 为什么测试环境数据量远小于生产?→ 没有关于测试环境数据量最低标准的规范,历史上测试环境从来没有做过代表性数据量的性能验证
贡献因素:
- 数据库连接池没有配置告警(连接使用率超过80%时没有告警,只有完全耗尽时才告警)
- 没有自动化的慢查询检测工具集成到 CI/CD 流程
改进措施:
- 在 CI/CD 中集成慢查询检测(EXPLAIN分析),有高风险查询时阻断发版 [负责人:工程效能团队,DDL:本月底]
- 测试环境数据量提升到生产的10%,并建立数据量最低标准规范 [负责人:DBA,DDL:下个迭代]
- 数据库连接池使用率超过70%时增加告警 [负责人:运维团队,DDL:本周]
看到区别了吗?好的复盘,根因指向了系统性问题(没有代表性测试数据、没有慢查询自动检测),改进措施指向了流程和工具,而不是"某个工程师要注意"。
这样的改进措施,才能真正防止类似问题再次发生,无论是谁来写这段代码。
复盘频率和时间节点
什么时候做复盘,做多长时间,也是需要规范的。
立即复盘(故障恢复后24小时内): 先写一个简短的初步复盘,记录基本事实(时间线、影响范围、已采取的措施),不追求完整,但要快。趁着记忆新鲜,把关键事实记下来。
深度复盘(故障恢复后2-5天内): 有了足够的时间搜集信息、分析日志、做5 Why 分析之后,完成完整的复盘文档,开复盘会议。
时间不要拖太长: 超过一周没有做复盘,很多细节会被遗忘,而且紧迫感下降,团队对故障的重视程度也会下降。
