故障处理不止于恢复 ── 写给小型开发团队的 Postmortem(再发防止)实践模板

· 更新日期: · · 故障排查, 日志设计, Postmortem, 再发防止, 运维, 维护, Windows开发, 技术咨询, 判断表

「上个月修好的那个错误,又在别的画面上出现了」——在维护中的系统上被这样问到时,是否曾经语塞、不知该如何回答?

故障应对本身——也就是检测、定位原因、修复、发布、道歉——这几步大多数团队都能做得很到位。问题出在这之后。故障一恢复,所有人立刻回到日常工作,故障的记录就散落在某个人的邮箱和聊天记录里,逐渐被遗忘。半年后,结构相同的故障在别的地方再次发生,又要把同样的调查从头做一遍。「修复、道歉,就此结束」的故障应对,是一种一再缴纳同一笔学费的运营方式

本博客此前已经在故障调查的技术层面,通过「Windows 崩溃转储采集入门」与「异常的 catch・日志与错误处理实务」两篇文章,讲解过「如何追溯到原因」的方法。本文要处理的是下一道工序:在已经查明原因之后,该如何持续开展复盘(Postmortem),才能不让同样的故障再次发生。这里不谈大规模 Web 服务,而是聚焦在一个 2~5 人的小团队维护业务应用、Windows 软件时,实际能够坚持下去的方法论。

1. 先说结论

  • 复旧・原因究明・再发防止是三项不同的工作。复旧是「让今天的业务恢复正常」,原因究明是「能够说明清楚为什么会发生」,再发防止是「改变机制」。三者混在一起做,结果都会做得不上不下。
  • 不追究个人责任(blameless)不是出于道德,而是出于实际利益。在追究个人责任的场合,信息就不会被说出来,也就无法追溯到根本原因。假设「相关人员都是在自己当时掌握的信息范围内做出了正确的判断」,然后去寻找机制上的漏洞——这正是在 SRE 领域确立下来的 blameless postmortem 原则。1
  • 再发防止策略要落到机制(代码・测试・监控・流程)上,而不是「多加注意」。依赖人的注意力的对策,会随着负责人的更替而消失。第 6 章给出用于判断对策强度的判断表。
  • 把原因拆分为「直接原因」和「寄与要因」。能真正修复的,大多是寄与要因这一侧;「五个为什么(なぜなぜ分析)」的诀窍在于不要止步于某个人的行为,而是要一直挖到机制层面。
  • 不需要为所有故障都做完整版的 Postmortem。按影响程度 × 再发可能性进行分级(第 7 章),轻微的故障只需留下一段记录即可。把工作量控制在能够持续下去的范围内,是这项制度不被废弃的最大前提条件。
  • Postmortem 的篇幅要控制在 1 小时能写完的程度。第 4 章给出最小模板。与其一个月写出 0 篇像样的文档,不如每次都留下一份粗略的记录,后者更有价值。
  • 在委托开发中,面向客户的故障报告书要与 Postmortem 区分目的,但内容可以从 Postmortem 转用而来(第 8 章)。不把追究责任的文档和再发防止的文档混在一起,才能同时守住两者的质量。

2. 为什么同样的故障会反复发生

再发生的故障,存在的往往不是技术层面、而是运营层面的固定模式。

故障一旦恢复,就被当作「已经结束」。故障应对属于紧急插入的工作,一旦恢复,所有人都会回到堆积如山的日常工作中。复盘的时间从未被排进任何人的日程表,「等忙完这阵子再做」永远不会真的到来。

报告书以「今后会多加注意」收尾。在提交给客户或上级的报告书的再发防止一栏中,写上「会彻底确认」「会进行双重检查」之类的话就此收尾。这些措辞并没有改变任何机制,于是在注意力逐渐松懈的几个月后,又会掉进同一个坑里。

把问题归咎于个人,而不修复结构性问题。如果用「是那个人漏做了测试」来了结这件事,那么让测试可以被漏做的结构——发布流程里没有测试关卡、在截止日期压力下省略步骤被默许——就会原封不动地保留下来。下一次,换一个人做出同样的省略。

记录没有留下,数年后又掉进同一个坑。小团队的维护工作之所以「即使没有记录也能暂时运转」,恰恰是因为一直由同一个人在处理。但这个人的记忆会在数年间逐渐淡忘,并在离职或换人时彻底消失。「这个错误好像以前见过,但想不起来当时是怎么处理的」——如果有记录,原本 10 分钟就能查清楚的事情,会因此变成耗费一整天的调查。

以上这些都不是能力问题,而是复盘从未被定义为一项正式工作所带来的结果。正因如此,提前定好一套模式(模板与实施标准)才有价值。

3. 什么是 Postmortem——把 SRE 的模式翻译给小规模维护

Postmortem(事后复盘)是一种复盘模板,用来记录事故的影响、根本原因、应对的时间线以及再发防止措施,它是通过 Google 的 SRE(Site Reliability Engineering,站点可靠性工程)实践而被广泛确立起来的。其核心是 blameless(不追究个人责任) 原则。「以不追究责任的方式写成的 Postmortem,会假设所有相关人员都是出于善意、基于当时所掌握的信息做出了正确的行为」——与其惩罚个人,不如去修复那个妨碍了正确行为的机制。1

这种理念并非 Google 独有。微软的架构指南(Azure Well-Architected Framework)同样将 Postmortem 定义为「由所有相关团队参与的、结构化的不追责评审」,并建议把根本原因分析(RCA)的结果,以改进应对流程、加强检测(可观测性)、改进设计这三种形式反馈回系统中。2

有人容易认为「我们又不是大规模 Web 服务,这跟我们没关系」,但笔者认为恰恰相反。在没有客户现场常驻人员、由少数人维护桌面应用程序的体制中,Postmortem 反而更能发挥作用。理由有三点。

  1. 由同一个人持续处理,一旦没有记录就会完全依赖个人。在大型组织里总会有别人记得,但在 2~3 人的团队里,「记得的那个人」就是唯一的数据库。Postmortem 会成为这个团队的外部记忆。
  2. 故障之间的间隔较长。与 Web 服务不同,业务应用程序的重大故障一年只发生几次。等到上一次应对的记忆已经淡去,下一次故障才姗姗来迟,这使得记录的相对价值更高。
  3. 正因为无法亲赴现场,证据与记录才成为生命线。在远程维护中,「当时到底发生了什么」的事后重建能力决定了一切,而这与 Postmortem 的时间线记录是一脉相承的。

另外,作为该不该写 Postmortem 的参考标准,SRE 相关书籍中给出的实施标准例子包括用户可见的服务中断、数据丢失、需要 on-call(紧急值班)介入等情形。1适合小规模团队的标准,会在第 7 章重新整理。

4. 最小 Postmortem 模板

能否坚持下去,最重要的条件就是篇幅。下面给出一份 Markdown 模板,设计上限是初稿要能在 1 小时内写完。建议将其以带日期的文件形式,放在仓库中的 docs/postmortem/ 之类的目录下,与代码在同一个地方进行版本管理。

# Postmortem:订单列表中出现跨日期的数据重复显示(2026-07-15)

- 状态:已完成 / 行动进行中 / 仅完成登记
- 撰稿人:小村
- 严重程度:中(业务得以继续,但产生了人工核对工作)

## 概要(3 行以内)
在月结处理执行期间打开订单列表,前一天的单据出现了重复显示。
仅为显示层面的问题,数据库中的数据本身是正常的。

## 影响(谁・什么・多大程度)
- 受影响者:销售事务人员 3 名
- 影响内容:列表画面重复显示。险些造成一起重复发货的事故 1 起
- 时长:7/15 9:10 左右 〜 11:40(约 2.5 小时)

## 时间线
- 09:10 用户来电反映「同一张单据显示成了两行」(检测到)
- 09:30 远程共享画面,确认复现条件
- 10:15 临时应对:请用户在月结处理期间不要打开列表
- 11:40 发布修复版本,确认已恢复

## 直接原因
列表查询用 UNION ALL 的方式,把月结处理复制到临时表中的行
与正式表的数据合并在一起(没有做互斥控制)。

## 寄与要因
- 月结处理与画面查询的并发执行,不在测试场景之内
- 临时表的存在没有写入设计文档,画面一侧改造时未被考虑到
- 没有检测重复显示的机制,发现完全依赖用户

## 做得好的地方
- 用户记录了操作日志的时间,使得复现条件的定位很快完成
- 发布机制已经自动化,修复版本能够当天完成部署

## 再发防止行动(负责人与期限)
- [ ] 为列表查询增加重复检测断言(小村,7/22)
- [ ] 为月结处理与查询系统的并发执行增加测试(小村,7/29)
- [ ] 探讨废弃临时表方式、改用快照隔离(小村,8 月底前确定方针)

写作时有几个要注意的点。

  • 「概要」控制在 3 行以内。日后来查找这份文档的人,是未来的自己。搜索到后能在 3 行之内看懂内容,比章节结构是否漂亮更重要。
  • 时间线只写带时间戳的事实。解释(本应…、应该…)要分离到原因部分。一旦解释混入时间线,事后阅读时就无法重建当时到底发生了什么。
  • 一定要写「做得好的地方」。这可以防止复盘变成检讨大会,也是把「碰巧顺利」的部分(比如刚好有日志留存)升格为正式机制的入口。
  • 行动项一定要写明负责人和期限。没有负责人和期限的行动不会被执行。Postmortem 的实际效果,取决于它是否被评审、行动是否被追踪。1

5. 原因分析的实务——区分直接原因与寄与要因

模板中把原因栏分成两部分,是有原因的。

直接原因(direct cause) 是直接引发这次故障的技术性事件。比如「NULL 检查遗漏导致异常」「没有互斥控制的 UNION ALL」。修复补丁修的就是这一层。

寄与要因(contributing factors) 是允许直接原因混入、被忽视、或使损害扩大的那些条件。比如「没有针对那种情况的测试」「没有写进设计文档」「检测完全依赖用户」。再发防止策略的主战场就在这里,通常会有多个。

之所以要分开,理由很简单:即使只修复了直接原因,只要寄与要因还在,另一个直接原因就会通过同样的路径再次混入。开头提到的「同样的错误出现在别的画面上」正是如此。

5.1 「五个为什么」不要止步于「某个人的行为」

作为挖掘原因的工具,「五个为什么(なぜなぜ分析 / 5 Whys)」很有效,但如果挖掘方向不对,它就会变成一个追究罪魁祸首的工具。典型的失败,是止步于「为什么→因为负责人忘了确认」。不要止步于此,再往下挖一层。

  • 为什么会忘记确认 → 因为确认这件事既没有写进作业手册,也没有列入清单,只能靠记忆
  • 为什么要靠记忆 → 因为发布流程从未被文档化,每次都是临场组装出来的

当「某个人的行为」作为答案出现时,那不是终点,而是通往「追问机制」这个下一个问题的入口。如果接受「人一定会犯错」这个前提,那么值得深挖的问题就会从「为什么会犯错」变成「为什么这个错误会原封不动地进入生产环境」。

5.2 没有证据,分析就无从谈起

原因分析的质量,上限取决于故障发生时留下的证据的质量。无法重建时间线的 Postmortem,只会沦为一篇推测性的作文。对于 Windows 应用程序的维护来说,至少需要以下三样东西打底。

如果试着写 Postmortem 却发现时间线补不上,这本身就是一个寄与要因——「检测与记录的机制不足」,同时也是一条再发防止行动的候选项。

6. 再发防止策略的质量——强度判断表

把再发防止行动列出来之后,要判断它们的强度。判断的轴心是「在多大程度上依赖人的注意力」。

强度 对策类型 例子 效果的持续性
多加注意・进行周知 「彻底确认」「提醒邮件」「鼓励双重检查」 数周~数月。会随负责人更替而消失
作业手册・检查清单 发布检查清单、故障应对手册、评审要点表 只要流程被遵守就能持续。存在流于形式的风险
机械式地防止・检测 自动化测试、断言、由类型・设计带来的约束、CI 关卡、监控告警 只要机制在运转就能持续。不依赖人的状态

这里现实的规则是:不是「禁止使用弱对策」,而是「不要让弱对策成为最终答案」。作为可以当天完成的临时应对,进行周知是有意义的,但填在恒久对策一栏里的,至少应该是中等强度,如果可能,最好是强对策。

如果想到的只有弱对策,可以用下面这些问题重新审视。

  • 「即使换成一个刚入职的新人处于同样的情况下,这起故障是否依然不会发生?」——如果答案是否定的,那就还没有形成机制。
  • 「能不能让编译器、测试或 CI 中的某一个检测出这个错误?」——比如,如果问题是「关闭时把异常吞掉了」,那么强对策不是进行周知,而是把类似「想定外异常时选择退出还是继续的判断表」中整理的方针,作为共用的异常处理器实现在代码里。
  • 「能不能让人根本无法犯这个错误?能不能让人更快察觉到这个错误?」——如果防止的成本太高,转而依靠检测(监控、告警、核对批处理)也是一种合格的强对策。把故障的教训反馈为更强的检测与更好的设计,微软的指南中也推荐同样的思路。2

强对策需要花费较多的工时,实务上比较可行的做法是,把「这周就能完成的中等对策」与「下个月要做的强对策」都写进行动列表,用期限来管理。

7. 该对哪些故障做——实施的分级

如果强制要求对所有故障都写完整版 Postmortem,3 个月之内就会没人再写了。要按影响程度 × 再发可能性来决定实施的等级。

  容易再发・结构性问题 不容易再发・一次性问题
影响大(业务停止・数据损坏・波及客户) 完整实施:模板全部项目 + 相关人员参加的评审会(30 分钟) 完整实施(仅文档。评审会可选)
影响中(靠临时应对维持了业务) 简易实施:仅模板中的概要・原因・行动项 在故障管理台账中留一段记录
影响小(用户不会察觉・轻微的显示错乱) 在故障管理台账中留一段记录,并按季度回顾一次趋势 在故障管理台账中留一行记录

运营上有三个要点。

  • 「同类故障再次发生」,无论影响程度如何,都要提升一个等级。因为一旦再次发生,就证明上一次的对策没有真正形成机制。
  • 即使轻微,也一定要留下记录。一段话就够了。只要有「日期・现象・原因・应对」这 4 项内容,数年后的自己就能通过搜索得救。小故障的记录积累多了,还能看出「就这个画面故障特别多」这类结构性的偏差。
  • 拿不准时就倾向于写,但要压缩篇幅。与其花时间纠结要不要做完整版,不如先动手写简易版,反而更快结束。

8. 在委托开发中的处理方式——与面向客户的故障报告书的关系

在委托开发或维护合同中,故障发生后,客户往往会要求提供一份「故障报告书」。提前理清 Postmortem 与报告书之间的关系,可以避免重复劳动。

其中约 8 成的内容可以直接转用。概要、影响、时间线、直接原因、再发防止策略,正是面向客户的报告书的构成要素。如果按照「先写内部用的 Postmortem,再从中编辑出面向客户的版本」这个顺序来做,就不会出现只为了报告书而单独撰写的内容。

不过,要意识到这是两份目的不同的文档,并把它们区分开来。

视角 内部 Postmortem 面向客户的故障报告书
目的 改变机制,防止再发 履行说明责任,维持信任
读者 未来的自己・团队 客户的负责人・其上级
原因的写法 坦率地写到寄与要因为止(包括公司内部流程上的不足) 准确陈述事实,专业术语要转译
责任・赔偿 不写(作为 blameless 范围之外的内容单独处理) 依据合同另行整理(通常也是与报告书不同的另一份文档)
再发防止策略 带负责人与期限的行动项 已完成 + 计划中的内容,并附带期限

之所以要分开,最大的理由是一旦把追责・赔偿的讨论与再发防止的讨论混在一起,两者都会被扭曲。在牵涉责任问题的文档中,相关人员难免会写得比较防御性。防御性的文档中,寄与要因会消失,再发防止也会退化成「会多加注意」。反过来,如果把坦率撰写的内部 Postmortem 原封不动地交给客户,脱离上下文的段落有时也会被过度解读、独自发酵。比较安全的做法,是把「坦率书写的内部文档」和「准确传达的外部文档」分开,做成从前者单向生成后者的流程。

另外,一旦在面向客户的报告书中写下了计划实施的再发防止策略,这就成为对客户的一项承诺。应当用与 Postmortem 行动项相同的机制来管理期限,完成后再向客户报告。这一来一回,反而能把一次故障变成积累信任的机会。

9. 总结

  • 故障应对不会止步于恢复。要把复旧・原因究明・再发防止当作三项不同的工作,把复盘(Postmortem)纳入正式的工作流程之中。
  • Postmortem 的原则是 blameless(不追究个人责任)。追究个人责任,信息就不会被说出来,也就无法追溯到根本原因。分析不要止步于某个人的行为,要一直挖到机制上的漏洞(寄与要因)。1
  • 文档只需要一份能在 1 小时内写完的最小模板(概要・影响・时间线・直接原因・寄与要因・做得好的地方・带负责人与期限的行动项)就足够了。作为补全时间线的前提,要提前做好崩溃转储日志事件日志这几类证据的保全。
  • 再发防止策略要从「多加注意」(弱)提升到作业手册(中),如果可能,进一步提升到用测试・断言・监控・设计变更来机械式地防止(强)。把教训反馈到检测与设计中的这套思路,是 SRE 与微软两方指南所共有的。12
  • 不要对所有故障都做完整实施。按影响程度 × 再发可能性进行分级,即使是轻微的故障,也一定要留下一段记录。
  • 在委托开发中,应先写内部用的 Postmortem,面向客户的故障报告书再从中编辑而来。把追责・赔偿的文档与再发防止的文档区分开,才能同时守住两者的质量。

相关文章

相关咨询领域

合同会社小村软件承接从难以复现的故障排查・原因解析,到构建证据保全(日志・转储采集)机制,再到包含再发防止在内的维护运营体系梳理等一系列工作。从「同样的故障反复发生」「故障报告书中的再发防止策略已经流于形式」这类阶段开始咨询,我们也欢迎。

参考链接

  1. Google, Site Reliability Engineering: Chapter 15 - Postmortem Culture: Learning from Failure。关于 blameless postmortem 的原则(假设相关人员都是出于善意、基于当时所掌握的信息做出了正确的行为)、Postmortem 实施标准的例子(用户可见的服务中断、数据丢失、on-call 介入等)、评审与行动追踪的重要性,以及追责文化会导致信息被隐藏这几点。  2 3 4 5 6

  2. Microsoft Learn, Architecture strategies for designing an incident management (IcM) process - Azure Well-Architected Framework。关于将 Postmortem 定义为「由相关团队参与的结构化不追责评审」、根本原因分析(RCA)是包含寄与要因在内的根本原因识别,以及建议把 RCA 的教训归类反馈到改进应对流程、加强可观测性(检测)、改进工作负载设计这三个领域中去。  2 3

共享相同标签的最新文章。可以围绕相近的主题进一步加深理解。

与本文相近的主题页面。以本文为起点,可以进一步了解相关服务和其他文章。

本文与以下服务页面相关联,欢迎从最接近的入口查看。

常见问题

汇总了咨询这一主题时常见的问题。

什么是 Postmortem?它和故障报告书有什么区别?
Postmortem 是指在故障恢复之后进行的一种结构化复盘文档与活动,是在 SRE(Site Reliability Engineering,站点可靠性工程)领域中确立下来的做法。它以不追究个人责任(blameless)为前提,记录时间线、直接原因、寄与要因以及再发防止措施。面向客户的故障报告书是一份履行说明责任的文档——「发生了什么、如何应对的」,而 Postmortem 则是一份用来决定「今后要如何改变机制,才能不再发生同样的事情」的内部文档。不过,由于两者内容大部分是重叠的,更高效的做法是先写 Postmortem,再从中编辑出面向客户的报告书。
再发防止策略总是变成「今后会多加注意」。该怎么办?
「多加注意・进行周知」依赖于人的记忆与善意,因此其效果会随着时间推移和负责人更替而必然消失。在思考对策时,请重新问自己:「即使换成一个刚入职的新人处于同样的情况下,这起故障是否依然不会发生?」如果答案是否定的,那就还没有形成机制。强有力的对策,是那些无需依靠人的注意力、就能被机械式检测或防止的东西——测试、断言(assertion)、由类型或设计带来的约束、监控告警等。如果暂时无法立即落实强有力的对策,可以先把清单或作业手册作为过渡阶段,并将根本对策作为带有期限的行动记录下来。
在小规模的委托开发中,没有余力为所有故障都写 Postmortem。
并不需要为所有故障都写完整版的 Postmortem,如果勉强这样做,这项制度本身也无法持续下去。应该按影响程度与再发可能性对是否实施进行分级(triage):对于导致业务停止、数据损坏的故障,以及同类故障再次发生的情况,进行完整实施;对于轻微的故障,只需在故障管理台账中留下一段记录即可,形成这样的浓淡区分。重要的是「即使轻微,也一定要留下记录」——数年后同样的现象再次出现时,能否检索到过去的记录,会让调查所需的时间产生巨大差异。
不追究个人责任(blameless)是不是意味着把责任问题含糊带过?
并非如此。blameless 是一项原则,它把关注点从「是谁犯了错」转移到「为什么这套机制会让这个人犯错」上。如果是操作员操作失误,那么在容易出错的 UI 或流程中就存在寄与要因。在惩罚个人的组织里,下一次故障发生时信息就会被隐藏起来,导致永远无法追溯到根本原因。另一方面,面向客户的说明责任(发生了什么、如何进行赔偿)应当作为另一份文档、另一套流程来履行,这与 blameless 的复盘并不矛盾。把追究责任的文档与再发防止的文档分开,才是实务上的关键所在。

作者简介

本文作者的个人简介页面。

Go Komura

小村软件有限公司 代表

以 Windows 软件开发、技术咨询与故障排查为中心,擅长难以复现的故障调查,以及既有资产仍在运行的项目。

返回博客列表