拒绝甩锅与走过场:一页纸极简复盘模板,让系统故障不再重演
我看过无数的故障复盘文档(Postmortem),说实话,大部分都是为了写给一群根本不存在的人看的。
这些文档动辄五六页,开头有个给高管看的“执行摘要”(但高管根本不会点开),中间有个根本原因分析(最后草草归结为“人为错误”),结尾列了一堆像“改善监控”这样的行动项(没指定负责人,没截止日期,最后不了了之)。
结果就是,八个月后同样的故障再次发生,有人问“等等,我们上次不是复盘过吗?”,然后全网搜不到那份文档。
记住,故障复盘只有一个核心目的:让下一次故障的恢复时间更短,或者彻底避免它再次发生。 文档里的每一个字都必须为这个目的服务,否则就是在演戏。
经过无数次“演戏”后,我们总结出了一套真正管用的格式。它只有一页纸,不到一小时就能写完,而且大家真的会去看。模板我放在文章底部了,直接拿去用。
毁掉复盘文档的三个致命错误
1. 表面说着“不追责”,实际在“甩锅”
大家都喊着“对事不对人”,但在时间线里却把某个人的名字提了十几次。这里有个实用的测试方法:把文档里所有的人名替换成职位(比如“值班工程师”、“代码审查员”)。如果替换后故事逻辑就不通了,说明你的复盘是在针对某个人,而不是针对系统。系统是可以修复的,但羞辱个人只会让大家学会隐藏下一个错误——而隐藏的错误,就是小故障演变成大灾难的元凶。
2. 写得太晚
故障发生两周后,大家的记忆会自动把过程“美化”成一个更顺畅的故事。但那些 messy(混乱)的真相——比如花了十分钟排查错误的服务、被大家随手划掉的报警提示——才是最宝贵的经验,而这些细节往往最先被遗忘。所以,哪怕复盘会议还没开,也请在 48 小时内把时间线草稿写出来。
3. 根本还原不了真相
这是结构性问题。一场故障的线索散落在报警工具、错误追踪器、三个聊天群、一个私聊窗口和部署日志里。要拼凑出真实的时间线,简直像在五个工具里做考古。所以很多人干脆跳过这一步,凭感觉瞎写。其实你不需要什么高级工具,只要养成一个习惯:在故障处理过程中,随手把时间戳和关键操作粘贴到文档里。未来的你会感谢现在勤快的自己。
文档里到底该写点什么?
带时间戳的时间线(包括那些尴尬的弯路)
不要写成记叙文,要写成时间轴。问题到底是什么时候开始的(注意:不是什么时候被发现的,这两者的时间差是你最重要的发现之一)。什么时候第一次被人工注意到?大家尝试了什么?什么方法没奏效?
走弯路的过程才是重中之重。 “我们花了 25 分钟重启 API,最后才发现是数据库的问题”——这绝不是什么丢人的废话,而是整篇文档里复用价值最高的发现。因为如果你不写下来,下一个人绝对会踩进同一个坑里。
灵魂拷问:“为什么我们的防护机制没拦住它?”
你可以不玩“连问五个为什么”的游戏,但绝对不能跳过这个问题。每一个能跑到生产环境的 Bug,都是穿透了你所有的防线:测试、代码审查、预发环境、报警、限流。Bug 本身往往不是最有趣的,防线上的漏洞才是最有趣的。
- 为什么测试没拦截住?(是没覆盖到?还是这个场景根本无法测试?)
- 为什么代码审查没看出来?(是代码改动太大?审查者缺乏上下文?还是大家只是在走过场点赞?)
- 为什么报警没在用户发现问题前响起来?(这个问题最扎心,但也最能让人学到东西。)
注意这些问题的共同点:它们都在关注系统。这才是真正的“对事不对人”——不是“没人需要负责”,而是“系统失败了,而系统是我们唯一能改变的东西”。
最多 3 个行动项
这是我们格式中最强硬的规定,也是我最坚持的一点。
一份列了 12 个行动项的复盘文档,最后往往 0 个能完成。12 个任务意味着没有优先级,最后全被扔进需求池吃灰。必须强迫自己排序:哪 3 个改变在“防止复发”和“投入精力”的性价比最高? 每个任务必须指定一个具体的负责人(是具体的人,不是某个团队,团队负责等于没人负责)和明确的截止日期。
至于其他你想到的点子,统统放进“考虑过但不打算做”的列表里,并写明原因。这个列表其实是文档里最诚实的部分,它能防止下次故障时,大家又把那些已经被否决的想法拿出来重新吵一遍。
一句话说明:“下次我们怎么提前发现它?”
就写一句话。如果答案是“跟这次一样——等客户来骂我们”,那就老老实实写上去。把这句话写下来会让人很不舒服,但正是这种不舒服,才能逼着大家把监控漏洞给补上。
极简复盘模板
把下面这个模板复制到你们的 Wiki 里。克制住想往里面加东西的冲动——篇幅越长,越没人看。
# [YYYY-MM-DD] Short, searchable title (name the system + symptom)
# [日期] 简短、易搜索的标题(系统名称 + 故障症状)
**Impact:** Who/what was affected, for how long, how badly.
One sentence. Numbers if you have them.
# 影响:谁或什么受到了影响,持续了多久,有多严重。用一句话概括,有具体数据就写数据。
**Detection gap:** Issue started HH:MM - detected HH:MM - by [alert / customer / luck]
# 检测缺口:问题开始时间 - 发现时间 - 发现途径 [系统报警 / 客户反馈 / 碰运气]
## Timeline (UTC)
# 时间线 (UTC),记录关键节点,千万别漏掉走弯路的过程
- 09:12 : Deploy #482 ships (部署 #482 上线)
- 09:14 : Error rate on auth-svc rises, no alert fires (auth服务错误率上升,未触发报警)
- 09:31 : First customer report (收到首个客户反馈)
- 09:40 : On-call restarts API, wrong tree, 25 min lost (值班人员重启API,找错方向,浪费25分钟)
- 10:05 : DB connection pool identified as cause (确认数据库连接池为根本原因)
- 10:11 : Mitigated via rollback (通过回滚缓解问题)
## Why our safeguards missed it
# 为什么我们的防护机制没有拦截住它
- **Tests:** … (测试:为什么没测出来?)
- **Review:** … (代码审查:为什么没审出来?)
- **Alerts/monitoring:** … (报警和监控:为什么没提前报警?)
## Root cause
# 根本原因
2–4 sentences. If it ends at "human error," keep digging —
what made the error easy to make and hard to catch?
# 写2到4句话。如果结论停留在“人为错误”,请继续深挖——是什么机制让这个错误容易发生且难以被发现?
## Action items (max 3)
# 行动项(最多3个),必须明确具体负责人和截止日期
1. [Change/具体要做的改变] - Owner: [Person/具体人名] - Due: [Date/截止日期] - Done: [Yes/No/是否完成]
## Considered but not doing
# 考虑过但不打算做的方案(记录原因,避免下次重复讨论)
- [Idea/想法] — [reason we're accepting this risk/我们接受此风险的原因]
## Next time, we'll detect this via
# 下次我们将通过什么方式提前检测到它
One sentence. Be honest if the answer is "we won't."
# 一句话。如果答案是“我们检测不到”,请诚实写出来。
看到这些“反模式”请直接删掉
- 把“人为错误”当根本原因。 这通常是分析偷懒的终点,绝不是真相的终点。
- 把“改善监控”当行动项。 改善哪个监控?阈值设多少?谁来负责?这种模糊的任务永远无法验收,也就永远没人去做。
- 文档变成“Word坟墓”。 如果复盘文档不能在工程师日常工作的地方被轻松搜到,那它就不存在。文档标题必须包含“系统名”和“症状”,因为下次值班的兄弟在凌晨 2 点 panicked(恐慌)搜索时,搜的就是这两个词。
- 只在大故障时才写复盘。 20 分钟的“险些出事”和 4 小时的“全面宕机”能教你同样的道理,但前者的成本低了 95%。小故障也要复盘,这是最便宜的学费。
总结
如果你的复盘文档又长、又晚、又喜欢甩锅、还搜不到,大家就会把它当成一种“惩罚性 paperwork(文书工作)”——那你得到的分析质量,也就只配得上这种惩罚性文书。
把它们变成一页纸、快速写完、聚焦系统、易于搜索,它们就会成为你所能做的最便宜的系统可靠性投资。
直达网址:https://flowtux.com/blog/introducing-flowtux-for-on-call
