跳转到主要内容
FDEFDE Wiki

在不能随手发版的地方排障

客户环境抽掉了工程师最依赖的两样东西:复现和快速迭代。四个约束,以及仍然能找到故障的那套动作顺序。

更新于 2026-10-301 分钟阅读

在客户现场排障,和在自家排障是同一件事,只是少了两条腿。你通常复现不了,因为没有他们的数据;你通常也迭代不了,因为一次发布要走变更单、等窗口、找签字。剩下的只有一门手艺:不运行代码也能把范围收窄。

到底哪里不一样

你看不到整个系统。 日志是部分的,保留期是三十天或者三十分钟,而那条有意思的请求落在一个不归你管的组件里。本能反应是要更多日志;更好的第一句话是问:这条缺失的日志会改变哪个决定——有一半的时候,它哪个决定都不会改变。

时间不是你说了算。 每停一小时,都是一场你不在场的会里的业务事件。这让工作的形状变了:你要找的不是那个 bug,而是下一个最便宜的测试。

复现掌握在一个人手里。 财务某个同事收到那条格式错乱的记录,而系统日志里什么都没留下。拿到他的屏幕,或者他手上那三条例子,比读一小时日志有用。

修复有运输成本。 热修意味着工单、回滚方案和一场沟通。所以只在你能说出原因时才发修复;说不出来,就发一个仪表。

动作顺序

按顺序做,一旦某一步能区分出假设就停下。这套顺序的意义在于:每一步都用比上一步更低的代价,把范围砍掉一半。

  1. 确认现象,而不是确认报告。 三个人说的「很慢」可能是三件不同的事。要一个时间戳、一个标识、一张截图。然后核对系统是否也认为那个时刻发生了什么事。
  2. 找到还正常的那条边界。 最后一个成功案例在 10:00,第一个失败在 10:20,那么 10:11 的那次变更就是嫌疑人,不管谁说它做了什么。
  3. 确认请求到底有没有到。 你的端点返回 200,只证明你的处理函数跑了,不证明动作完成,更不证明目标端收下了。这一条区分能解释掉相当大一部分「集成坏了」的工单。
  4. 读数据,不要读代码。 在客户环境里,数据错的频率远高于逻辑。拿那个失败的标识,手动走一遍管道;你的预期和那条记录分岔的地方,就是答案。
  5. 只加一个仪表,然后等。 加最小的、能把剩下两个假设分开的观测点,并且放在客户也能看见的地方。只有你能读的仪表,会把你变成永久依赖。
  6. 一次只改一处。 在客户系统上,两个同时进行的变更会产生一个谁都归不了因的结果,而且被怪罪的永远是第二个。

看着像 bug 的三件事

这三类出现得足够频繁,值得在任何堆栈之前先排掉。

重复执行。 事件投递两次、非幂等的重试、定时任务自己跟自己重叠。症状看起来是间歇性的,并且和量相关——所以人们会去找竞态条件,然后找一周。

过期的配置。 环境变量改了,进程没重启;或者三个节点里重启了两个,而负载均衡是轮询。

时钟。 时区不一致的时间戳、UTC 凌晨两点过期的证书、偏差一小时的缓存。这一类制造出最有说服力的「它就是随机的」报告。

边查边写什么

事情进行中就在事故文档里记时间线,带时间和谁说了什么。两个理由:复盘失败多半是因为序列缺失;全程看着你记录东西的客户,会比看着你敲键盘的客户更相信最后的解释。

每一行都要分清观察到和推断出来的。「队列的请求在 10:11 掉到零」是观察。「worker 挂了」是推断,而且可能错——worker 也许活着,只是连不上队列。

与技术无关的那部分

报问题的人通常是害怕的,而屋子里声音最大的那个,往往不是掌握信息的人。两句话承担了大部分工作:我们在处理,现在知道的是这些,下一次给你消息是什么时候。 一个守时的下一次更新,比一个迟到且没有预告的修复更值钱。

事情结束之后,复盘必须产出一个变更,而不是一段叙述。如果产出是一份写着「人为失误」的文档,那么什么都没学到——人为失误是所有事故的共同描述,和任何一起事故的具体原因都无关。事故对部署能不能活下来的影响,是四周法则的事;该通知谁、什么时候通知,属于升级路径。

来源与延伸阅读

本文为 FDE 百科的原创分析。源自他处的框架与案例材料已在下方标注出处。