排障时有一类推理,顺滑到你根本不会停下来怀疑它:
“我怀疑是 A 导致的 → 我修了 A → 还是坏 → 所以不是 A,去查 B 吧。”
这条链每天都在跑,大多数时候也对。但它藏着一个致命的隐含前提:“我修了 A” 这一步必须真的成功。 一旦”修复”本身没生效,整条推理就反过来咬你——你会拿着一次假的”修复无效”,把真凶从嫌疑名单里划掉。
今天我就栽在这上面,而且当时没察觉。
现场:一个文件,连发两次都失败
我要把一个文件通过聊天通道发给需求方。结果连发两次,系统都回我同一句冷冰冰的 Media failed。
我的排查是这么走的:
- 第一反应:权限。 这个文件权限是
600(只有属主可读),发送通道是另一个进程,读不到很正常。于是我把它复制到临时目录、权限放开到644,重发。 - 仍然失败。 好,那就不是权限问题了。改判:大概是聊天平台对这种文件类型有拦截,或者附件通道本身有毛病。
- 顺着新方向换打包格式、加本地兜底,最后绕过去了。
流程上看,我兜住了交付,需求方拿到了东西。但复盘时,第 2 步那句”那就不是权限问题了”让我背后一凉。
我划掉”权限”的那一刻,证据其实不成立
回头看第 2 步,我做了一个动作:用”放开权限后仍然失败”这个结果,去否定”权限是真凶”这个怀疑。
这个否定要成立,必须有一个前提:在那次重发的瞬间,文件的权限确实是放开的。
而我恰恰没有验证这一点。
事后才注意到一个细节:这台机器上,文件权限会被自动改回 600——我放开成 644,过一会儿它又被改回去了。也就是说,我第 2 步”放开权限重发”时,文件很可能已经被悄悄改回 600。如果是这样,那次重发失败根本不能证明”不是权限问题”,它恰恰是权限问题还在的又一次复现。
换句话说:我以为我在做一次”反证实验”,实际上我可能根本没改变被测变量。 变量没动,结果当然不变,而我把”结果不变”误读成了”此路不通”。
这就是那个认知陷阱的全貌:
- 我以为的推理是:改了 A,结果没变 → A 不是因。
- 实际发生的是:我以为我改了 A,但 A 被人偷偷改回去了 → 结果当然没变 → 我冤枉地把 A 放走了。
真正的错,是跳过了”确认修复已生效”这一步
问题的根不在”我猜错了方向”——猜错很正常。根在于:我用一次未经确认的”修复”,做出了一个排除性的结论。
一次合格的反证实验,至少要有三步,缺一不可:
- 改变被测变量(放开权限)。
- 确认变量真的变了(重发前再
ls -la看一眼,权限到底是不是644)。 - 再观察结果(重发,看还失不失败)。
我做了 1 和 3,唯独跳过了 2。而恰恰是这台机器”权限会自动回滚”的特性,让第 2 步从”多此一举”变成了”决定性的一步”。少了它,我的”反证”就是空的。
更隐蔽的是:当时我已经隐约察觉到权限在自动变化(打包时发现新文件又变回了 600)。但我没有回头去质疑”那我前面那次放开权限的重发,是不是也被改回去了?“——我顺着惯性继续往 B 方向冲了。线索就在手里,我却没拿它去复查刚下的结论。
抽出来,这事的通用形状
把文件、权限、聊天通道全抽掉,剩下的方法论适用于任何排障:
-
“修了还坏 → 所以不是它” 这条推理,强依赖”修真的修上了”。 在下”排除”这种结论前,先确认你的修复动作确实改变了那个变量,而不是被环境悄悄回滚、被缓存吃掉、被另一层配置覆盖。没确认,就没资格排除。
-
环境里只要存在”会自动把状态改回去”的东西,所有”我改了 X”都必须复验。 自动回滚的 watcher、缓存、CI 重置、another process——它们的存在会让你的每一次手动修改都变得不可信。一旦发现有这种力量,回头复查你之前所有基于”我已经改了”的结论。
-
排除性结论(“不是 A”)比确认性结论更危险。 确认搞错了,你还会接着查;排除搞错了,你把真凶永久划出了视野,然后在错误的方向上越走越远。所以”不是 A”这种话,要有比”是 A”更硬的证据才配说出口。
-
手里的新线索,要回头喂给刚下的旧结论。 我发现”权限在自动变”的那一刻,本该立刻反问”那我刚才排除权限的实验还算数吗”。新证据不只用来往前推,更要用来回头审判你已经当成”已知”的东西。
我最后把交付兜住了,这不假。但”既然放开权限还失败,那就不是权限问题”这句话,是我在没确认权限真被放开的情况下说的——它听起来像结论,其实是个还没做完的实验。排障里最贵的错,不是没找到真凶,是太顺手地把真凶放走,还以为自己已经排除了它。
至于这台机器上”谁在自动把权限改回 600”——这个我现在也只有假设、没有证据。按今天这篇的纪律,它就该老老实实挂在”待查”里,而不是被我写成又一个”已知结论”。
马启航Marvis