agentsclimarketplace

Debugging

Skill Lion-1209/Lion-Skills/skills/debugging

面向开发者的 Claude Code skills 套件 | A developer-focused Claude Code skills suite

Install
npx -y skills add Lion-1209/Lion-Skills --skill debugging

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

遇到 bug、报错、崩溃或行为异常,需定位根因时。

SKILL.md

8.9 KB, as published. Nobody here has run it

Debugging

概述

用科学方法定位 bug 的根因,而不是凭直觉乱改碰运气。核心:调试是观察 → 假设 → 设计实验验证 → 缩小范围 → 定位根因的循环,每一步都有依据。改到"不报错"不算修好——那可能只是把症状盖住了,根因还在。

何时使用

  • 遇到报错/崩溃/行为异常,要定位原因
  • bug 不稳定复现,不知触发条件
  • 调试卡住、改了好几处都不行
  • 看了代码"没发现问题",但程序确实有问题

不该用:行为符合预期的"不是 bug"(先确认是不是 bug,别把功能当 bug 调);环境/部署问题(先排查是不是代码外原因)。

与相邻 skill 的边界debugging定位根因(调查:复现、假设、二分、读堆栈),verify-and-fix修复与验证(改对:修病因不症状、防回归)。两者接力——debugging 找出"问题是什么、在哪",verify-and-fix 接手"怎么改对、怎么确认修好"。debugging 的终点(定位的根因)就是 verify-and-fix 的起点。

当 bug 报告太模糊时("页面打不开""功能不工作"却没给报错/复现步骤),先回到 clarifying-questions 的思路——要可观察的现象(报错信息、触发操作、环境、是所有情况还是特定情况)再分析。没有现象的调试就是盲猜。

核心内容

第一原则:先看报错,别跳过它直接猜

报错信息(异常类型、消息、堆栈、行号)往往直接包含根因线索,是调试最便宜的情报。最常见、最浪费的错法是不看报错就凭直觉改——明明堆栈第 3 行写着 Cannot read 'id' of undefined at line 42,却跳过它去猜"是不是网络问题""是不是缓存"。

读报错的顺序:

  1. 异常类型 + 消息:发生了什么(TypeError?NullPointer?超时?)
  2. 第一个你自己代码的堆栈帧:在哪发生的(行号 + 函数)——注意是"你的代码",不是框架/库内部的帧
  3. 触发上下文:什么操作/数据触发的

读堆栈的技巧:堆栈常被框架/异步包装得很难读,几个技巧帮你找到真正的根因帧:

  • 跳过框架帧:堆栈顶部往往是一堆框架内部代码(React 调度、Express 中间件、ORM 反射),真正的根因在第一个属于你项目源码的帧——往下翻找到你认识的文件名/行号。
  • 异步代码的堆栈可能不连续async/await、Promise、回调、事件循环的堆栈经常断开(一个错误在 setTimeout 里抛,堆栈却看不到触发它的代码)。现代运行时有 --async-stack-traces 或类似的异步堆栈支持,开启它;否则要在触发处手动打日志补全调用链。
  • 错误被转发后原始堆栈会丢:如果错误被 catch 又重新抛(尤其改了消息或包了新异常),原始堆栈可能藏在 error.causeoriginalError 里——别只看最外层,挖嵌套的 cause 链。

养成习惯:遇到 bug,第一件事是完整读一遍报错,而不是打开编辑器开始改。读不懂报错时,先查懂它(搜异常类型、读文档),别跳过。

先复现,再调试

不能稳定复现的 bug 几乎无法调试——你改了不知道有没有效,因为"不报错"可能是修好了,也可能是这次没触发。调试前先建立可复现

  • 找到触发 bug 的最小条件:什么输入、什么操作顺序、什么状态组合下必现?
  • 最小化:剥离无关因素,直到只剩"做 X 就必崩"。最小复现让你能反复试验、验证修复。

不稳定复现的 bug(偶发)尤其要先攻克复现——它通常意味着有隐含条件没找到(并发时序、特定数据、资源竞争、时间相关)。找这个条件本身就是定位根因的关键。

反例:bug 偶发,你直接多加几个 try/catch 把可能出错的地方包起来"这样就不崩了"——错误被吞了看不见,但触发条件和根因一行没动,换个场景又炸,而且现在连报错都没了,更难查。

科学方法:假设 → 实验 → 验证

定位根因靠假设驱动,不是碰运气:

  1. 观察:报错是什么、何时发生、复现条件。
  2. 假设:"我猜根因是 X"——基于观察和代码理解提出具体、可证伪的假设(不是"大概是哪里有问题")。
  3. 设计实验:如果是 X 导致的,那应该观察到 Y(可验证的预测)。
  4. 验证:跑实验,看 Y 是否成立。成立 → 假设支持,继续深入;不成立 → 排除这个假设,换下一个。
  5. 缩小范围:每次实验排除一部分可能性,把根因锁定在更小的范围。

关键纪律:一次只验证一个假设。同时改多个变量,有效了也不知道是哪个起作用——这是 shotgun debugging(散弹枪式乱改)的典型。

什么样的假设是好假设:调试假设要具体且可证伪——能说"如果是 X 导致的,那应该观察到 Y",并且能设计实验验证 Y。

  • 差的假设:"大概是缓存的问题""可能是哪里有 bug"——模糊、无法验证,等于没假设,只会让你乱试。
  • 好的假设:"我猜是用户列表为空时 find 返回 undefined 导致后续 .name 抛错——如果是,那传一个空数组应该必现,传非空不报。" 具体(空数组触发)、可证伪(传非空应该不报)、能设计实验(构造两种输入对比)。

提不出具体假设,说明你对问题观察得不够——回到读报错、看运行时真实数据、建立复现,先把现象搞清楚。

二分法:范围太大时快速缩小

当代码量大、不知问题在哪时,用二分法(git bisect 是经典实现):

  • 注释/禁用一半代码,看 bug 还在不在
  • 还在 → bug 在剩下的一半里;消失了 → bug 在被禁用的那一半里
  • 对剩下一半重复,每轮砍一半

二分法把"在 10000 行里找 bug"变成"log₂(10000) ≈ 13 次实验"。它不依赖理解代码,纯靠排除——当你读代码读不出问题时,二分是最有力的工具。

适用场景:回归 bug(之前好的现在坏了,用 git bisect 定位是哪次提交引入的)、不知问题模块的大型代码库、读代码陷入僵局。

回归 bug:用好"曾经正常"这个参照点

"之前好的,现在坏了"是最容易调的一类 bug——你有一个已知正常的版本作参照,省掉了"重建预期行为"的功夫。策略:

  • git bisect 是首选:在"好的"和"坏的"提交之间二分,每轮 git 自动 checkout 中间提交让你测,log₂(N) 次就能锁定引入 bug 的那次提交。这比读 diff 猜快得多。
  • 锁定提交后看 diff:那次提交改了什么,根因大概率就在 diff 里——范围从"整个代码库"缩到"一次提交的几处改动"。
  • 想清楚"为什么之前没事":很多回归 bug 的根因是"旧代码依赖了一个隐含假设,新改动破坏了它"。问"这个改动为什么会引发这个症状"往往直接指向根因,比泛泛调试快。

区分"代码问题"和"运行时问题"

"看了代码没发现问题"不等于"代码没问题"——也可能是运行时问题,代码逻辑没错,但运行时的数据/环境/状态让行为出错:

  • 代码问题:逻辑写错(看代码能发现)。
  • 运行时问题:数据不符合预期(null、空、格式错)、环境差异(依赖版本、配置、权限)、状态问题(时序、并发、缓存残留)、外部依赖故障(网络、第三方服务)。

调试时两者都要查。如果代码读起来"没问题",转向查运行时:实际数据是什么样?打印/日志看一下真实值,而不是盯着代码猜。很多 bug 是"代码假设数据是 A,实际数据是 B"——光看代码发现不了,看真实数据立刻暴露。

常见错误

问题修法
不看报错就凭直觉猜第一件事完整读报错(类型/消息/堆栈/行号)
用 try/catch 把错误盖住当"修好"那是掩盖症状,先定位根因(见 verify-and-fix)
乱改试错(同时改多处)一次只改一个变量、验证一个假设
不能复现就硬调先建立最小复现,否则改了也不知道有没有效
改到"不报错"就停不报错≠修好,可能只是症状被盖住,查清根因
只读代码不看运行时数据代码没问题可能是运行时问题,打印/日志看真实值
陷入循环(A↔B),继续硬改停,回到稳定状态,换方法(二分/重新假设)
范围太大读代码读不出用二分法快速缩小,不依赖理解代码
回归 bug 逐行读 diff 猜用 git bisect 在好/坏提交间二分,锁定引入提交再看那一次的 diff

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.