诊断
针对严重错误的规范。仅在明确合理的情况下跳过阶段。
在探索代码库时,使用项目的领域词汇表来获得相关模块的清晰心理模型,并检查你正在接触的区域中的 ADR。
第一阶段 — 构建反馈循环
这就是技能所在。 其他一切都是机械性的。如果你有一个快速、确定性、代理可运行的错误通过/失败信号,你就会找到原因——二分法、假设测试和检测都只是消耗该信号。如果你没有这样的信号,再多的盯着代码看也救不了你。
在这里投入不成比例的努力。要积极主动。要有创造性。拒绝放弃。
构建方法——大致按此顺序尝试
- 失败的测试在任何能够触及错误的接缝处——单元测试、集成测试、端到端测试。
- Curl / HTTP 脚本针对正在运行的开发服务器。
- CLI 调用带有固定输入,将标准输出与已知良好的快照进行差异比较。
- 无头浏览器脚本(Playwright / Puppeteer)——驱动 UI,对 DOM/控制台/网络进行断言。
- 重放捕获的跟踪。 将真实的网络请求/负载/事件日志保存到磁盘;孤立地通过代码路径重放它。
- 一次性 harness。 启动系统的最小_subset_(一个服务,模拟依赖项),通过单个函数调用练习错误代码路径。
- 属性/fuzz 循环。 如果错误是"有时输出错误",运行 1000 个随机输入并查找失败模式。
- 二分法 harness。 如果错误出现在两个已知状态之间(提交、数据集、版本),自动化"在状态 X 启动,检查,重复",以便你可以
git bisect run它。 - 差异循环。 通过旧版本与新版本(或两个配置)运行相同的输入并差异输出。
- HITL bash 脚本。 最后的手段。如果必须有人点击,使用
scripts/hitl-loop.template.sh驱动_他们_,以便循环仍然结构化。捕获的输出反馈给你。
构建正确的反馈循环,错误就已经 90% 修复了。
迭代循环本身
将循环视为产品。一旦你有了_一个_循环,问:
- 我能让它更快吗?(缓存设置、跳过不相关的初始化、缩小测试范围。)
- 我能让信号更清晰吗?(断言特定症状,而不是"没有崩溃"。)
- 我能让它更确定吗?(固定时间、种子 RNG、隔离文件系统、冻结网络。)
一个 30 秒的不稳定循环 barely 比没有循环好。一个 2 秒的确定性循环是调试超能力。
非确定性错误
目标不是干净的重现,而是更高的重现率。将触发器循环 100 次,并行化,添加压力,缩小时间窗口,注入睡眠。50% 的抖动错误是可调试的;1% 则不可——不断提高比率直到它可调试。
当你真的无法构建循环时
停止并明确说明。列出你尝试过的内容。向用户请求:(a) 访问重现它的任何环境,(b) 捕获的工件(HAR 文件、日志转储、核心转储、带时间戳的屏幕录制),或 (c) 添加临时生产检测的权限。在没有循环的情况下不要继续进行假设。
在你相信有一个循环之前,不要进入第二阶段。
第二阶段 — 重现
运行循环。观察错误出现。
确认:
- 循环产生用户描述的失败模式——而不是恰好 nearby 的不同失败。错误的错误 = 错误的修复。
- 失败在多次运行中是可重现的(或者,对于非确定性错误,以足够高的比率可重现以进行调试)。
- 你已经捕获了确切的 symptom(错误消息、错误输出、缓慢计时),以便后续阶段可以验证修复是否真正解决了它。
在你重现错误之前不要继续。
第三阶段 — 假设
在测试任何假设之前生成3-5 个排名假设。单假设生成会锚定在第一个看似合理的想法上。
每个假设必须是可证伪的:陈述它做出的预测。
格式:"如果 是原因,那么 <改变 Y> 将使错误消失 / <改变 Z> 将使它更糟。"
如果你不能陈述预测,该假设就是一种感觉——丢弃或 sharpen 它。
在测试之前向用户显示排名列表。 他们通常拥有可以立即重新排名的领域知识("我们刚刚部署了对 #3 的更改"),或者知道他们已经排除的假设。便宜的检查点,大大的时间节省者。不要阻塞——如果用户不在,继续你的排名。
第四阶段 — 检测
每个探测必须映射到第三阶段的特定预测。一次改变一个变量。
工具偏好:
- 调试器/REPL 检查如果环境支持。一个断点胜过十个日志。
- 目标日志在区分假设的边界处。
- 永远不要"记录所有内容并 grep"。
用唯一前缀标记每个调试日志,例如 [DEBUG-a4f2]。最后的清理变成单个 grep。未标记的日志存活;标记的日志死亡。
性能分支。 对于性能回归,日志通常是错误的。相反:建立基线测量(计时 harness、performance.now()、分析器、查询计划),然后二分。先测量,后修复。
第五阶段 — 修复 + 回归测试
在修复之前编写回归测试——但仅当有正确的接缝时。
正确的接缝是测试在调用站点发生时练习真实错误模式的地方。如果唯一可用的接缝太浅(当错误需要多个调用者时的单调用者测试,无法复制触发错误的链的单元测试),那里的回归测试会给出虚假的信心。
如果不存在正确的接缝,这本身就是发现。 注意它。代码库架构正在阻止错误被锁定。将此标记为下一阶段。
如果存在正确的接缝:
- 将最小化的重现转换为该接缝处的失败测试。
- 观察它失败。
- 应用修复。
- 观察它通过。
- 针对原始(未最小化)场景重新运行第一阶段反馈循环。
第六阶段 — 清理 + 事后分析
在声明完成之前必需:
- 原始重现不再重现(重新运行第一阶段循环)
- 回归测试通过(或记录接缝的缺失)
- 所有
[DEBUG-...]检测已删除(grep前缀) - 一次性原型已删除(或移动到清晰标记的调试位置)
- 结果正确的假设在提交/PR 消息中陈述——以便下一个调试者学习
然后问:什么可以防止这个错误? 如果答案涉及架构变更(没有好的测试接缝、纠缠的调用者、隐藏的耦合),将 specifics 移交给 /improve-codebase-architecture 技能。在修复到位之后提出建议,而不是之前——你现在拥有的信息比开始时更多。