调试策略
采用可复现方法(二分、隔离、假设)而非试错式微调。
为什么
随机微调——注释行、换值、重启服务——感觉高效但极少找到根因。它们靠巧合掩盖 bug(重试消失)或把它移到别处(症状变但原因在)。可复现方法把调试从迷信变工程:你提假设、设计可证伪实验、留日志避免重复死胡同。最大省时是 git bisect——log(N) 步找到引入 bug 的精确提交而非读 diff。无方法的团队在硬 bug 上多花 3 倍时间,且常以两周后回归的 方式'修'它。
何时使用
第一个硬 bug 之前。学方法 30 分钟,每 bug 省数小时。永不跳过。
如何做
1. 可靠复现——不能复现的 bug 不能修 - 写触发它的失败测试;不能则缩步骤直到能 - 记精确环境(node 版本、env、DB 状态)非"我机器上能跑" 2. 提假设并写下再行动 - "空引用因用户在 profile 迁移前创建" - 写下的假设可证伪;模糊直觉不可 3. 隔离变量——一次改一个 - 禁一半中间件;bug 在则因在另一半 - git bisect 找引入提交:git bisect start && git bisect bad HEAD && git bisect good v1.0 4. 检视非假设——读实际值 - console.log / debugger 在边界非深处 - 查 DB 行、网络响应、env——bug 通常在数据跨边界处 5. 修因非症——验证测试过且持续绿 - 加回归测试让此 bug 永不回 - 若修是"加 null 检查",问为何值是 null 并修那
示例
# 复现:触发 bug 的失败测试
test("迁移前创建的用户不崩", () => {
const user = factory.user({ createdAt: "2024-01-01", profileId: null });
expect(() => renderProfile(user)).not.toThrow();
});
# bisect 找引入提交
git bisect start
git bisect bad HEAD # 当前坏
git bisect good v1.4.0 # v1.4.0 好
# git 检出提交;你标好/坏;100 提交约 7 步缩范围
# 假设日志(调查时留 DEBUG.md)
# 2024-03-15 14:30 H1: 空 profileId——证伪,v1.2 起有守卫
# 2024-03-15 14:45 H2: user.create 与 profile.create 竞态——证实常见问题
- ⚠一次改 3 处,bug 消失,不知哪个修的——后回归。
- ⚠dev 热重载调试,模块状态过时 bug 不复现——浪费 1 小时追幻影。
- ⚠读代码非运行时值——bug 几乎总在代码实际做而非看似说。
- ⚠跳回归测试,3 月后重构重引入同 bug。
最佳实践
- ✓每调查留 DEBUG.md:时间戳、假设、实验、结果——把 4 小时追变 bug 回时 30 分钟 读。
- ✓早用 git bisect;免费且对数时间找精确提交,无需读 diff。
- ✓修在 PR 同加回归测试,链接永久可审。
- ✓修是守卫(null 检查、try/catch)时,再花 10 分钟找坏值为何到守卫——守卫藏真 bug。