---
name: technical-postmortem
description: 严肃技术故障复盘、事故通报（Postmortem）与对外公关的表达规范。基于防御性工程与无指责复盘（Blameless Postmortem）原则，剔除空洞道歉与甩锅推诿，下钻到机制防线与量化事实。触发："事故通报 / 写复盘 / postmortem / 线上故障复盘 / 宕机通报 / 故障原因分析 / 为什么挂了 / 事故报告"。
---

# 严肃技术故障复盘与事故通报规范

事故复盘与故障通报不是“公关道歉信”，更不是“道德审判与甩锅大会”。**它的唯一目的是：用不可辩驳的客观事实还原真相，把根因锁定在系统机制与物理约束的缺失上，并给出一经部署就不可绕过的物理防复发措施。**

判断一份事故报告是否合格的唯一标准：**读完之后，用户和工程师是觉得“这家公司在遮遮掩掩推卸责任”，还是清楚地知道“系统在哪个物理环节断了、这次教训换来了什么永久防线”？**

---

## 零、事故通报六步骨架（The Postmortem Skeleton）

动笔撰写故障通报或技术复盘前，按此骨架填空，不缺步、不跳步：

```
① 首屏摘要   故障定级 · 影响范围（受损比例/脏数据行数/时长） · 核心根因一句话
② 时间线     UTC 时间戳驱动 · 状态变化 · 发现/止血/根治的客观动作（零修辞）
③ 根因下钻   5 Whys 机制下钻 · 归因于机制缺失，绝不下钻到“某员工疏忽”
④ 止血处置   当时采取的应急措施 · 恢复线上可用的临时绕行方案
⑤ 永久防线   Action Items · 谁主责 · 何时上线 · 依赖的物理门禁/代码约束
⑥ 外部证据   监控图表 · 日志指纹 · Git Commit SHA · 外部健康探测回执
```

---

## 一、🚫 事故通报四大禁忌（Anti-PR Blacklist）

任何准备发给用户、客户或管理层的事故通报，严禁出现以下“公关八股”：

| 违规表述 | 为什么令人极其反感 | 工业级替代方案 |
|---|---|---|
| **“给您带来的不便敬请谅解”** | 万能无意义套话，用户受损时听到这话只觉冷漠与傲慢。 | 删掉！直接陈述补偿方案或已就绪的恢复动作（“受影响账单已全额回退，补偿配额已到账”）。 |
| **“我们将深刻吸取教训，举一反三”** | 语言的巨人、行动的矮子。没有任何工程落点。 | 给出代码级约束（“代码已引入 pre-commit 语法门禁，同类配置错误将在提交时被直接拒绝”）。 |
| **“由于某工程师操作不当 / 某同事失误”** | 把系统性漏洞甩给个人，暴露团队工程素养极差。 | 归因于防护机制缺失（“系统缺乏操作二次确认与沙箱隔离栅栏，允许未经验证的脚本直连了生产库”）。 |
| **“极少数用户受到轻微影响”** | 掩耳盗铃、试图粉饰太平，被抓出真实数据直接信誉破产。 | 给出精确客观数字（“共影响 142 个并发请求（占同时段总流量的 0.18%），持续 14 分钟”）。 |

---

## 二、关键段落表达规范

### 1. 影响面量化（Impact Scope）
- **绝不使用模糊字眼**：禁止写“部分用户”、“短时间”、“个别接口”。
- **必须提供四维指标**：
  - **持续时长**：起止时间点（精确到秒）。
  - **受损流量比**：5xx 错误请求数占总请求数的百分比。
  - **业务受损量**：失败的订单数、延迟的消息条数或受污染的数据库行数。
  - **数据安全性确认**：数据是否发生泄漏？是否发生不可逆丢失？（如无，明确声明“未发生数据丢失或越权泄漏”）。

### 2. 时间线还原（Timeline）
时间线必须是纯粹的事实流水账，每一个节点只包含两部分：**时间戳 + 发生了什么**。

```text
- 14:02:11 UTC：自动化部署任务 #412 开始向生产集群灰度推送 v2.4.1。
- 14:03:45 UTC：新加坡节点网络流量触发 502 警报，错误率升至 8.2%。
- 14:05:00 UTC：值班工程师接入，确认网关与数据库连接池打满。
- 14:07:30 UTC：执行一键回滚指令，切回 v2.4.0 镜像。
- 14:11:15 UTC：全节点流量恢复 200，监控指标回落至健康基线。
```

### 3. 根因下钻（Root Cause & 5 Whys）
下钻的终点必须是一个**“客观缺失的机制”**，而不是一个**“主观犯错的人”**。

```text
现象：线上网关发生 502。
Why 1: 为什么 502？因为数据库连接池耗尽，新请求被拒绝。
Why 2: 为什么连接池耗尽？因为迁移脚本 008 执行了一条全表关联的大查询。
Why 3: 为什么执行了大查询？因为缺少慢查询超时配置，单个事务挂起超过 60 秒。
Why 4: 为什么没有超时配置？因为上线前没有在 staging 环境执行生产级流量回放。
Why 5（根因）: 系统缺乏自动化性能回归门禁，允许未经性能基准测试的 SQL 直接合入主干。
```

### 4. 永久性行动项（Action Items）
行动项必须遵循 **SMART 原则**，每一条必须有唯一负责人与截止时间，且必须是**物理阻断机制**（代码门禁、系统参数、定时巡检），不能是自觉要求。

| 改进方向 | 伪整改（自觉要求 ❌） | 真防线（物理机制 ✅） | 责任人 | 截止时间 |
|---|---|---|:---:|:---:|
| 数据库防护 | 要求大家以后写 SQL 注意加 limit | DB 网关强制配置 `statement_timeout = 8s`，超限自动掐断 | @vex | 2026-10-26 |
| 门禁拦截 | 提醒提单人不要漏写必填项 | CLI 引入字段校验器，缺块直接拒收 | @nanzhu | 2026-10-25 |

---

## 三、正反案例对比

### ❌ 假人公关推脱体
> **关于今日系统故障的情况说明**  
> 尊敬的用户：今日下午由于不可抗力的网络抖动及内部工作人员误操作，导致部分用户的系统访问受到轻微影响。故障发生后，我司技术团队高度重视，第一时间进行了紧急排查与修复，目前所有服务均已恢复正常。对此我们深表歉意，给您带来的不便敬请谅解！未来我们将深刻吸取教训，举一反三，进一步加强运维管理，为大家提供更优质的服务！

### ✅ 工业级无指责复盘
> **2026-10-24 任务调度中枢 503 故障分析与整改报告**  
> **一、故障概述**  
> 2026-10-24 03:10:00 至 03:18:29 UTC（共 8 分 29 秒），生产调度网关出现全线 503，交付请求失败率达 98.2%。受损请求共 419 笔，均已在服务恢复后完成幂等补偿重试，无数据丢失。  
> 
> **二、根因剖析**  
> 1. 南烛节点历史遗留的分钟级兜底 Cron 与 Cloudflare 定时任务并发打库，触发 PostgreSQL advisory 锁竞争锁死。  
> 2. `xact advisory` 锁生命周期绑定到外部事务 COMMIT，导致代码块内无法提前释放，阻塞后续所有写入连接。  
> 3. 监控脚本对代理超时的判据错误，将死线超时误判为目标账号封禁，触发了自杀式循环重试与熔断。  
> 
> **三、止血与整改措施**  
> 1. **止血**：03:14:10 手动下线南烛残留 Cron，应用迁移脚本 009 拆分维护事务独立提交，连接池恢复。  
> 2. **物理防线（已上线）**：  
>    - 提交 `f07745d`：引入调度互斥分布式租约，租约超期自动回收，杜绝单机僵尸进程竞争；  
>    - 迁移 010：重构锁粒度为行级乐观并发控制（`WHERE rev = $1`），彻底弃用事务级 advisory 锁；  
>    - 监控脚本已修正返回码匹配逻辑，明确区分网络超时与业务拒绝。

---

## 四、📋 Postmortem 扣分清单（Deduction Rubric）

发布任何事故通报或故障复盘前逐条核验，**命中任何一项扣 25 分（满分 100，低于 75 分重写）**：

| 违规维度 | 扣分项（Anti-pattern） | 扣分 | 为什么致命 | 修正方案 |
|---|---|:---:|---|---|
| **首屏量化** | **使用“部分用户/轻微影响/短时间”等模糊词** | -25 | 破坏专业信任感，显得刻意粉饰。 | 必须给出故障起止秒数、受影响请求准确数量/百分比、脏数据量。 |
| **虚假客套** | **出现“敬请谅解/深表歉意/不便之处”等空洞公关套话** | -25 | 纯情绪消耗，没有信息量，激化读者不满。 | 砍掉所有公关修辞，用具体的补偿方案与恢复事实说话。 |
| **归咎个人** | **归因于“员工疏忽/人为失误/经验不足”** | -25 | 掩盖系统缺陷，惩罚个人并不能阻止下一个人在同一个坑翻车。 | 下钻到机制缺陷：为什么系统允许未经审计的输入被直接执行？ |
| **整改无力** | **整改项写“加强培训/深刻反思/提高警惕”等自觉承诺** | -25 | 纯口头保证，事故必然会以换皮方式再次复发。 | 必须是物理约束（自动化门禁、超时断开、数据库约束、CI 拦截）。 |
