Engineering
诊断缺陷
针对疑难缺陷和性能回归的诊断循环。当用户说"诊断"/"调试"时使用,或报告出现故障/报错/崩溃/变慢等问题时使用。
诊断缺陷
针对疑难缺陷的方法论。仅在有明确理由时才跳过阶段。
第一阶段 —— 建立反馈循环
这就是核心技能。 其余的都是机械操作。如果你拥有一个严格的通过/失败信号——一个专门针对_该_缺陷会变红的信号——你就一定能找到原因。
在这里投入超常的努力。要大胆,要有创造力,绝不放弃。
构建反馈循环的方法 —— 大致按此顺序尝试
- 失败测试:在能触及缺陷的任何接缝处编写测试——单元测试、集成测试、端到端测试。
- Curl / HTTP 脚本:针对运行中的开发服务器发送请求。
- CLI 调用:使用固定输入,将标准输出与已知正常的快照进行对比。
- 无头浏览器脚本(Playwright / Puppeteer)——驱动 UI,对 DOM/控制台/网络进行断言。
- 重放捕获的追踪数据。 将真实的网络请求/载荷/事件日志保存到磁盘,然后通过代码路径单独重放。
- 临时测试工具。 搭建一个最小的系统子集,通过单次函数调用执行缺陷所在的代码路径。
- 属性/模糊测试循环。 如果缺陷表现为"有时输出错误",则运行 1000 次随机输入并查找失败模式。
- 二分法测试工具。 如果缺陷出现在两个已知状态之间,自动化"在状态 X 启动、检查、重复"的流程,以便使用
git bisect run。 - 差异比较循环。 对旧版本和新版本分别运行相同的输入,然后对比输出差异。
收紧循环
将反馈循环视为一个产品。一旦你有了_一个_循环,就要收紧它:
- 能让它更快吗?
- 能让信号更清晰吗?
- 能让它更确定吗?
第二阶段 —— 复现 + 最小化
运行循环,观察它变红——缺陷出现。
确认以下事项:
- 循环产生了用户描述的失败模式
- 失败在多次运行中可以复现
- 你已捕获到确切的症状
最小化
一旦循环变红,将复现缩小到仍然变红的最小场景。逐一裁剪输入、调用方、配置、数据和步骤。
第三阶段 —— 提出假设
在测试之前,先生成3–5 个排序的假设。
每个假设必须是可证伪的:说明它所做出的预测。
格式:"如果 <X> 是原因,那么 <更改 Y> 将使缺陷消失 / <更改 Z> 将使其恶化。"
在测试之前向用户展示排序后的假设列表。
第四阶段 —— 插桩
每个探测器必须对应第三阶段中的具体预测。每次只改变一个变量。
工具优先级:
- 如果环境支持,优先使用调试器 / REPL 检查。
- 在区分假设的边界处添加有针对性的日志。
- 切忌"记录所有内容然后 grep"。
为每条调试日志添加唯一前缀标签,例如 [DEBUG-a4f2]。
第五阶段 —— 修复 + 回归测试
在修复之前编写回归测试——但前提是有合适的接缝。
如果存在合适的接缝:
- 将最小化的复现转化为该接缝处的失败测试。
- 观察测试失败。
- 应用修复。
- 观察测试通过。
- 对原始场景重新运行第一阶段的反馈循环。
第六阶段 —— 清理 + 事后总结
声明完成前必须满足:
- 原始复现不再触发
- 回归测试通过
- 所有
[DEBUG-...]插桩已移除 - 临时原型已删除
- 在提交/PR 信息中记录了最终验证正确的假设