4.5 再见!Bug
从看到异常就凭直觉改代码走到证据驱动定位并把缺陷固化为测试,沿复现、假设、二分、修复与回归边界诊断 Bug。
学习目标
- 能沿固定复现、提出假设、缩小范围、验证修复和补回归测追踪一次证据驱动调试
- 能解释日志、断点、二分和最小化实验如何寻找最早偏离点,并区分相关性与因果性
- 能在正常、边界和故障场景中回答:同时修改多处后现象消失时,为什么仍不能宣称 Bug 已被修复
为什么需要这一机制
本页依据刘欣《码农翻身》(2018 年第 1 版)及出版社公开书目信息,独立重构 4.5 再见!Bug。正文、代码、图示、实验和练习都是本课程重新设计的教学材料,不复制原书正文、插图、练习答案或代码。
4.5 再见!Bug 不能停在“看到异常就改一行”。真实系统要解决的变化是从 看到异常就凭直觉改代码 到 证据驱动定位并把缺陷固化为测试;可执行机制是:调试从可复现输入出发提出可证伪假设,通过日志、断点、二分或最小化实验寻找最早偏离点,再补回归测试。
三个会让调试失真的陷阱
一个目录节点到调试证据
4.5 再见!Bug
在 ↡从可重现输入与首个偏离出发,用假设、范围缩小、修复验证和回归测试消除缺陷的证据驱动过程。 中,二分搜索可近似写成
它表达的是每次实验都应减少候选范围,而不是保证所有 Bug 都能机械二分。调试合同要求记录输入、版本、时间线、假设、实验结果和修复前后行为。
固定复现
↡固定输入、环境、版本、执行顺序和预期差异,使缺陷能在干净状态反复出现的阶段。是证据起点。没有稳定案例,就先最小化输入和环境,而不是凭日志猜测。
提出假设
↡根据首个偏离提出一个可以被实验支持或推翻的具体原因,并说明预期观测的调试阶段。把猜测变成可证伪命题。好的假设指定变量、位置、条件和如果为真应发生的变化。
缩小范围
↡用日志、断点、提交二分、依赖替换或最小化输入逐步减少可能原因集合的阶段。每次只改变一个边界,并保存实验结果。范围变小而不是代码改多,才是调试进展。
验证修复
↡在原始复现、边界和回归样本上证明修复消除首个偏离且没有引入新的行为差异的阶段。不能只看当前一次通过。修复前案例应失败,修复后应稳定通过,关联的错误和资源合同也应保持。
补回归测
↡把最小复现案例、预期结果和边界保护加入自动测试,使同一缺陷在未来变更中可快速发现的阶段。把一次调试经验变成系统记忆。测试名称和断言应说明缺陷,而不是只覆盖某个内部实现细节。
最小可重放实现
case = reproduce(fixed_input, fixed_env)
hypothesis = propose(first_deviation(case))
candidate = narrow(case, hypothesis)
assert verify_fix(candidate, case)
assert regression(case) == pass这段实现草图只表达调试合同,不复制原书叙事或代码。实际运行时应保存输入、版本、首个偏离、假设、实验范围、修复差异、回归结果和复位轨迹,使另一位读者能够从干净状态重放。
五步复核一次 Bug 修复
1. 固定复现输入与环境
记录版本、输入、环境、执行顺序和预期差异。先预测基线会在哪个节点偏离,再运行多次确认案例稳定,不要先修改生产代码。
Lab
Bug 复现与因果定位实验
先预测一个假设会缩小哪一段范围,再切换边界与多变量重试并补回归。
固定案例后逐步二分,修复首错并补入回归
case=fixed → hypothesis=tested → range=small → fix=verified → regression=pass
判定
accept:因果、修复和长期保护闭环
当前样本:可归因修复;保存输入、版本、首错、假设、实验范围、修复差异、回归结果和复位轨迹。
正常、边界与故障证据矩阵
| 样本 | 只改变的变量 | 预期判定 | 必存证据 |
|---|---|---|---|
| 正常 | 固定输入、环境和版本 | 复现稳定,修复后回归通过 | 案例、首错、假设、测试 |
| 边界 | 时间、输入范围、依赖或提交改变 | 缩小候选并明确行为差异 | 实验、日志、断点、范围 |
| 故障 | 同时修改多处并反复重试 | 拒绝无因果证据的“修复” | 全部差异、时间线、复位轨迹 |
故障诊断:先分开复现、假设与修复
- 复现侧:查输入、版本、环境、顺序和稳定性;案例不稳定时先最小化,不要开始猜根因。
- 假设侧:查首个偏离、可证伪条件和预期观测;最后日志不一定是起因。
- 范围侧:查二分、断点、依赖替换、提交和最小输入;每次实验都应减少候选。
- 修复侧:查原始案例、边界、回归、接口和资源;现象消失不等于根因已验证。
如果只在某个提交后出现,先做提交二分;如果只在压力或时间边界出现,固定时钟和最小输入;如果修复后又回归,检查回归测试是否表达真实合同。每次只改变一个假设,并重放可信基线。
术语表
名词解释
本章出现的专业名词,用大白话再讲一遍。
- 4.5 再见!Bug
从复现与首错出发,通过假设、缩小、修复和回归测试消除缺陷的过程。
- 固定复现
固定输入、环境、版本和顺序,使缺陷能在干净状态重复出现。
- 提出假设
针对首个偏离提出可被实验支持或推翻的具体原因。
- 缩小范围
用日志、断点、二分、替换或最小化逐步减少候选原因。
- 验证修复
在原始、边界和回归样本上证明首错消失且没有新差异。
- 补回归测
把最小复现案例和预期结果加入自动测试以防缺陷重现。
练习
练习
问题 1(4.5 再见!Bug): 为什么调试的第一步是固定复现,而不是直接修改最可疑的代码行?
问题 2(固定复现、提出假设、缩小范围): 一个缺陷只在最近十次提交中的某些版本出现,如何用二分减少范围?
问题 3(验证修复、补回归测): 修复后现象消失,但没有新增测试,为什么仍不能关闭缺陷?
本页小结
4.5 再见!Bug 的关键不是反复试错,而是固定复现、提出可证伪假设、缩小候选、验证修复并补回归保护。完成标准是保留首个偏离和因果实验,拒绝同时改多处的模糊修复,并证明复位后回归案例仍稳定通过。