ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

后见之明偏差如何毁掉复盘?用时间线与决策记录破解

后见之明偏差如何毁掉复盘?用时间线与决策记录破解

“hindsight is 20/20”,这句英文俗语我读第一遍的时候只觉得是在说“事后看一切清楚”。后来干了十几年研发,参与过几十场大大小小的复盘会,才真正理解“事后”这两个字的杀伤力。每当线上事故复盘,总有人拍桌子说“这个配置错误一眼就能看出来啊”;每当项目失利总结,又有人说“当初就不该选这个方案”。说这些话的人,往往正是当初拍板的人。问题来了:到底是这些人的判断力有问题,还是有一种叫hindsight的东西,在悄悄改写我们所有人的记忆和判断?

这个标题“hindsight”,往小了说,是一个认知心理学概念,往大了说,是工程团队复盘文化里最难缠的对手。这篇文章不打算讲空洞的大道理,我想用这些年踩过的坑、见过的事故、写过的复盘报告,把“后见之明”彻底拆开:它如何在你脑子里起作用、如何在团队里传染、用什么流程可以把它从负债变成资产。适合谁看?带团队的技术负责人、事故复盘的组织者、写postmortem报告的人,以及每一个想从失败里真正学到点东西的普通开发者。

1. 先认清hindsight的真相:它不是一个词,是一种认知陷阱

1.1 事后“上帝视角”是怎么产生的

举一个我亲历的例子。某年大促前压测,线上响应时间一直不稳定,团队开了三次会,最终决定“先上线,靠限流兜底”。大促当天,限流阈值配错,直接切走了将近三成流量,用户体验雪崩。事后复盘,所有人包括我自己脑子里都冒出一个念头:压测数据明明就在报表里,为什么当时没人把阈值算清楚?

复盘会开到一半,我们翻出了当时的会议记录,才发现压测数据存在哪个报表、应该怎么查看、关键指标怎么看,文档里压根没写明白。换句话说,不是“没人看见”,而是“信息摆在那里,但没有人被引导过去看”。事后之所以看得清,是因为事故本身给我们画了一条线——出了问题之后,你才知道该盯哪个数字。这就是hindsight的第一层运作方式:结果告诉我们什么重要,然后我们误以为我们当时就知道什么重要。

1.2 后见之明偏差的三个组成部分

认知心理学对后见之明偏差的研究已经有几十年。1975年Fischhoff那个经典实验里,研究者让被试预测某场历史战争的结果,告知真实结果后,要求他们回忆自己当初的判断,绝大多数人的回忆都向真实结果靠拢。后来的研究把这种偏差拆成三个成分:

  • 记忆扭曲:结果出来之后,你大脑里“当初的判断”已经被悄悄改写了。你以为你记得,其实那是新记忆,不是旧记忆。
  • 必然性:结果一旦确定,你会觉得它必然发生,其他可能性变得不可想象。选A成功了就认定A是对的,选B失败了就认定B从一开始就是死路。
  • 可预测性:你不仅觉得它必然发生,还觉得当时完全可以预见到。

这三个成分放到工程项目里,有一个共同的根源:信息不对称。做决策的时候,我们面对的是一个开放性问题,根本不知道哪个变量会爆;复盘的时候,我们面对的是一个已经知道答案的谜题,解法就摆在眼前。用解题者的视角去评价一个当时的迷局,得出“当时就该知道”的结论,是认知系统自动完成的事情,不需要刻意蠢,也不需要任何恶意。

1.3 为什么聪明人更容易马后炮

这里有一个反直觉的现象:团队里最聪明、经验最丰富的人,往往最容易掉进hindsight的陷阱。因为经验越足,脑子里可调用的“类似事故场景”越多,一看到结果,马上就能搜到一个匹配的模式,然后脱口而出“我早就说过”。

但那个“早就说过”,往往不是在这个项目里说的,而是在另一个项目、另一个场景,或者只是在自己脑子里预演过。这种模式匹配速度太快,快到他自己都信了。加上事后复盘的场合里,谁第一个说出“我当初就觉得不对”,谁就获得了某种道德优势——这又反过来强化了大家展示马后炮的冲动。

提示:判断一个人在复盘时说的是不是马后炮,只需要问一句——当时会议上你有没有提出这个风险?如果说没有,那他现在看到的“我早就觉得”,大概率是记忆改写后的产物。

2. 工程团队里hindsight的四张脸

2.1 代码评审里“这么明显怎么会漏掉”

讲一个印象深刻的案例。某Java服务,统计用户积分的模块,开发用Integer做了一个比较:判断一个积分值是否等于100。用的是if (maxScore == 100)。Java的Integer缓存范围是-128到127,在这个范围内==比较的是缓存对象,超过这个范围就走新的对象比较,==永远返回false。

这段代码在本地测试跑得好好的,因为测试数据全在缓存范围内。上线第三天,有用户积分超过127,兑换一直失败。事后代码评审人拍着桌子说:“这行代码这么明显,怎么会让它合进去?”

问题是,代码评审的时候,PR页面上几百行差异,这行代码混在一大堆正常逻辑里,没有任何评审流程会要求逐行朗读所有条件表达式。这不是“人不仔细”,而是评审流程根本没有设计成能发现这类问题。后来我们把强制代码走查从“看PR”改成“跑场景”——评审人必须用真实数据集跑一遍关键路径,这个改动直接消灭了一整类“事后看起来很蠢”的bug。

2.2 事故复盘成了问责现场

更大的问题出在事故复盘会上。我观察过太多次:事故报告一上来,root cause写得异常流畅——“因为XX配置错误,导致XX服务异常,进而触发XX告警,造成XX损失”。整条链条看起来天衣无缝,像是一开始就能算准的三段论。

但真实世界的事故往往多因并发。某次我们系统出缓存过期风暴,表面原因是缓存key设置不合理,深层原因是一条新版本上线时把缓存失效时间从24小时改成了10分钟,再深层是发布评审环节根本没讨论缓存参数。如果复盘只写“缓存key设计错误”,只会诞生一条改进项:把key改回来。三个月后再上线,另一个参数又炸了。

所以我后来定了一条规矩:事故报告的第一版禁止写root cause,只许写时间线。谁先写了因果判断,谁就回去重写。这个规矩听起来简单,执行起来阻力巨大,因为团队已经习惯了“直接给结论”的报告风格。

2.3 架构选型的“我早就说过”

架构选型是hindsight的重灾区。早年我们做过一个系统,一开始上了微服务架构,后来业务量没起来,运维复杂度却翻了几倍,最后又退回模块化单体。复盘时团队里出现了两种声音:一种是“当初就不该上微服务”,另一种是“当初应该更彻底地上容器平台”。

这两种声音本质上都是马后炮。真实情况是,当时的业务预估是现在真实体量的十倍,微服务是合理选择;后来业务没起来,复杂度才显得格外刺眼。这不是选错,是假设变了。hindsight最喜欢做的事情,就是拿结果去否定当初的决策,却丝毫不提当时的约束条件——人力有限、时间紧张、需要快速验证市场。

对抗这一点,唯一的办法是保留一份“当时的决策依据”。这也是我后面要重点说的ADR(架构决策记录)的由来。

2.4 需求变更后的“当初该想清楚”

需求侧的马后炮更隐蔽。产品上线一个月,用户没有按预期路径走,当初评审时被砍掉的功能反而成了主入口。复盘会上产品经理说“当初应该多花一周做用户调研”。这句话是对的,但没有任何建设性——因为多花一周做调研,也不一定能预测用户行为。用户行为本来就是涌现出来的,是在真实使用中一点点长出来的。

更好的做法是承认“当时基于有限信息做了合理判断”,然后去设计一个能更快验证用户行为的机制,比如灰度发布、埋点统计、用户访谈计划。也就是说,不要要求团队在每一个未知面前拥有上帝视角,而要设计一套“比用户跑得更快”的反馈系统。

3. 把hindsight从负债变资产:一套能直接照抄的复盘流程

3.1 步骤一:用时间线重建,禁止用结果倒推

后见之明最有效的解药,是时间线。不是“原因—结果”的逻辑链,而是“事件—时间—证据”的流水账。具体操作分三步:

  1. 拉出变更记录、监控曲线、告警记录、发布日志,按分钟排列。
  2. 每一条只描述事实,不写“因为”“所以”。
  3. 等时间线彻底列完,再开始做因果推断。

举个例子,一次线上事件的时间线可以长这样:

14:02 发布批次A,包含Nginx配置变更 14:03 P99延迟开始上升(120ms -> 180ms) 14:05 错误率突破1% 14:07 告警触发,on-call手机通知弹出 14:10 on-call开始查日志和最近变更 14:25 定位到Nginx配置变更,执行回滚 14:30 各项指标恢复

这份时间线本身已经讲清楚了大半问题。最神奇的是,一旦你禁止所有人写“因为”,人们会自动意识到:告警延迟了三分钟、定位花了十五分钟,这些才是真正值得改进的环节。如果写成“配置写错了,下次注意”,所有人都会把注意力放在那个已经被回滚的配置上,没人再关心告警链路为什么慢了三分半。

3.2 步骤二:写决策记录,给未来的自己留“当时视角”

复盘永远在解决过去的问题,你没法阻止十个月后的人对你今天的决策做马后炮。唯一能做的,是让未来的决策有据可查。这就是决策记录(Decision Log / ADR)的价值。

我用的ADR模板长这样:

  • 背景:我们面对什么约束、什么信息。
  • 候选方案:至少列出两个。
  • 决策:最终选择了哪个。
  • 理由:为什么选它,放弃了什么。
  • 预期:如果这个决策是对的,三个月后应该出现什么迹象。
  • 风险:当时已知的不确定性。

举一个真实例子:

背景:订单服务需要支持按日汇总导出,当前单表数据量约500万行,预计一年后翻三倍。 候选方案:A. 单表加索引直接查(省事,但大表聚合慢);B. 引入Elasticsearch(查询快,但增加运维复杂度);C. 每日离线预聚合到宽表(复杂度中等,查询只读宽表)。 决策:选C。 理由:数据量增长可预期,宽表方案在查询性能和运维复杂度之间最均衡;Elasticsearch对我们团队是全新组件,当前没有运维经验储备。 预期:上线三个月后,导出接口P99稳定在500ms以内。 风险:预聚合任务可能失败,需要可靠的重试机制。

几个月后回看这份ADR,如果导出接口变慢,正确的排查方向是先看预聚合任务是否按时跑完,而不是无脑归咎于“当初怎么不用Elasticsearch”。ADR本质上就是给未来的自己留一张“当时的地图”,免得未来的人拿着现在的GPS去评判一张十年前画的地图不精确。

3.3 步骤三:把信息分成三类:已知、未知、不可知

复盘进行到“当时为什么没发现”这个环节时,最容易陷入争吵。我的做法是让所有人停下来,把讨论中的信息做一次强制分类:

类型含义典型例子复盘时处理方式
已知当时信息就在那里,本应该被看见监控报表里已经能看出异常走势检查流程为什么没让人看见,改流程
未知当时没人知道,但通过更多测试可以知道缓存参数变更导致雪崩,压测没覆盖补测试、补压测、补监控
不可知当时客观条件下不可能知道用户会在毫无征兆的路径上操作不追责,记录为经验

这个分类的威力在于,它把“谁蠢”的问题,转化成了“信息当时在哪里”的问题。绝大多数复盘会上,八成的争吵其实发生在“未知”和“不可知”的边界上。一旦大家把这俩分清,讨论立刻变得理性起来。

3.4 步骤四:改进项必须可验证、有owner、有deadline

复盘会的产出不是一份报告,而是几条能真正落地的改进项。我给复盘输出的改进项定了三条硬性规则:

  1. 每条改进项必须有明确的验证标准。比如“把缓存key规范补充进发布checklist”——验证标准是“下一次发布前checklist里有该条目且有人实际打勾”。
  2. 每条必须有owner。没有owner的改进项等于没写。owner不是“相关人”,是那个有义务在deadline前确认完成的人。
  3. 每条必须有deadline,而且要在复盘会上当场确认,不是会后补填。

另外,下一个迭代结束时,把改进项执行情况重新过一遍。没有闭环的复盘,本质上就是一场高级聊天——大家情绪到位、话说得很漂亮,但三个月后那条事故路径上的隐患依然原封不动。

4. 复盘里最容易翻车的五个细节

4.1 复盘太快或太晚

复盘会开得太早,事故当天情绪还在,容易变成互相甩锅现场;拖得太久,细节就模糊了,所有人都在靠记忆编故事。我自己的经验是:重大事故等24到48小时再开正式复盘会,给一晚上让大家冷静,同时留出翻日志、拉监控的时间;小问题一周内复盘。这样既不情绪化,也不丢失信息。

4.2 只复盘失败,不复盘成功

这个坑特别隐蔽。团队一说复盘就是失败复盘,成功上线就散会聚餐。但成功案例里同样藏着hindsight——成功了,大家会觉得自己当时判断英明,下一次就会滋长过度自信。我现在的做法是,每个季度挑一个相对困难的项目做“成功复盘”,重点问三个问题:当初我们最担心什么?哪些担心没有发生,为什么?有哪些坑是侥幸避过的?这样能把运气和实力分清楚,也能把成功的模式真正固定下来。

4.3 用现在的智商嘲笑过去的自己

复盘会一旦变成批斗会,措辞清一色都是“这么简单都没看到”。我每次听到这种话就会追问一句:当时如果有现在这些信息,你会做出不同的选择吗?如果会,那说明问题在机制,不在智商;如果不会,那现在也没有资格说“早就知道”。这句话可以贴在复盘会议室里,治标也治本。

4.4 改进项不设owner和时间点

绝大多数复盘报告都死在这一条上。报告洋洋洒洒写了三十多条建议,散会后没人认领,三个月后一切照旧。我现在要求任何复盘必须当场输出3条以内的改进项,并且当场把owner的名字和截止日期写在白板上。超过3条就砍,砍不掉的过流程专题讨论。因为一次复盘能真正执行落地的改进项,历史上看很少超过3条。

4.5 让复盘沦为形式主义的“填表”

有些团队的复盘流程化到离谱:填模板、打分、签字、存档。看起来严谨,实际上所有人都知道是在走流程。我见过最糟糕的复盘是所有人对着模板逐字念“我们的流程没有问题”“下次注意”。

让复盘真正有效的不是模板,而是敢说真话的氛围。这个氛围从哪来?从leader自己开始。如果leader在复盘会上说“这个问题是当时我拍板时没想到的,我的责任”,整个团队对复盘的态度会立刻发生转变;如果leader第一条改进项就分给自己,大家才会相信复盘不是走形式。

5. 我在实践中反复印证的三件事

最后以我个人的体会收尾。第一,hindsight最大的危害不是让过去显得愚蠢,而是让我们误以为“只要聪明一点就能避免所有失败”,于是放弃了真正对症的机制改进。个人再聪明,也比不上一个能记录决策过程的机制可靠。

第二,对抗hindsight不需要什么高深工具。一支笔、一张纸、一条“禁止写因为”的规矩,就能起效。我见过最好用的复盘工具,反而是一个简单的文本模板加一次严格的时间线重建。

第三,别指望一次复盘会改变一切。认知偏差会一次又一次回来,你只能一次又一次用流程去拽住它。但每拽住一次,团队就会离真实更近一点。以后再有同学对我说“这么明显为啥当初没看出来”,我就把他拉到时间线屏幕前,让他先回答一个简单的问题:事发当时,信息到底在哪里?

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进