调试不是玄学:我排障时的固定四步法
约 1 分钟· 988 字 1 次阅读
带团队时我常被问:"你是怎么这么快定位到问题的?"其实没什么天赋,只是我不靠"感觉",而靠一套固定流程。调试最贵的从来不是时间,是被假线索带偏的方向。
第一步:稳定复现,先别急着改
不能复现的 bug,等于没修。先回答三个问题:
- 在什么操作序列下必现?是"点一下就崩"还是"点十几下才崩"?
- 环境是否相关?浏览器版本、是否登录、数据是否为空?
- 能不能写一个最小复现(minimal repro)?
我踩过最大的坑之一:凭记忆"大概是这样操作就出错了",结果花两小时改了一通,发现复现路径根本记错了。先拿到一条能反复跑通的复现路径,再动手。
第二步:二分定位,把范围砍到最小
别从头读代码。用二分法快速锁定"坏掉的那一层":
- 前后端之间:先用 curl / 浏览器网络面板看接口返回对不对,确定是前端渲染错还是后端数据错;
- 函数之间:在关键节点打日志或断点,看数据在哪一步"变形";
- UI 问题:把样式一层层剥掉(或逐层加),定位是哪条规则在作妖。
一句话:让证据说话,而不是让猜测主导。
第三步:建假设,再验证
定位到范围后,写下你的假设——"我怀疑是 X 导致的",然后设计一个能证伪它的实验:
假设:列表错位是因为 flex 容器宽度被某个子项撑开
实验:给该子项加 max-width: 0,看错位是否消失
结果:消失 → 假设成立;没消失 → 推翻,换下一个假设
关键是一次只改一个变量。同时改三处然后"好像好了",你永远不知道是哪处生效,下次还会踩。
第四步:验证 + 防回归
修完别急着关。做两件事:
- 确认复现路径已不通,且在相邻场景(边界值、空数据)下也不崩;
- 留一个防回归的东西:一条单测、一段断言、或一个能一眼看出问题的日志。否则三个月后同样的坑还会再来。
那些年踩过的"假线索"
| 假线索 | 真相 |
|---|---|
| 报错栈指向的第三方程序库 | 实际是自己传了 undefined 进去 |
| "只在我机器上崩" | 其实是环境变量 / 缓存 / 依赖版本不一致 |
| 改了 A 就好了 | 其实 A 根本没生效,是 HMR 偷偷重载了 B |
| 日志没打印 → 没走到 | 可能是日志级别被过滤,而不是代码没执行 |
最阴险的是最后一条:我曾有次坚信"函数没被调用",反复查调用链,最后发现是日志级别设成了 warn,debug 全被吞了。证据不足时,先怀疑你的观测手段,再怀疑代码。
一个真实案例
有次一个 LAN 聊天界面偶发卡死。直觉告诉我"WebSocket 断了没重连",但复现后看网络面板——连接好好的。二分后发现是前端一个 setInterval 在收到高频消息时不断 setState,攒出成千上万个待渲染节点。
假设从"网络层"转向"渲染层",关掉那个定时刷新、改成消息到达时增量更新,卡顿消失。如果当初顺着直觉去改重连逻辑,只会越改越乱。
小结
我的调试四步法就四句话:
- 先稳定复现,拿不准就先别改;
- 二分定位,让证据说话;
- 建假设再验证,一次只动一个变量;
- 验证 + 留防回归,别让同一个坑来第二次。
技术栈会变,但这套"先证伪、再动手"的节奏不会过时。调试不是玄学,是把不确定性一个个关进盒子里。