关于 debugging
面向开发者和团队的调试技能,将排查过程组织为复现、取证、定位根因、修复和预防。内容还涵盖测试、升级处理及团队协作。
主要功能
- 01复现问题并收集证据
- 02提出假设并逐项验证
- 03针对根因修复并添加回归测试
- 04预防复发、记录过程与升级处理
适用场景
- 01排查生产告警或 QA 报告
- 02追踪不稳定测试和间歇性故障
- 03诊断性能下降或集成问题
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ krzysztofsurdy/ code-virtuoso --skill debugging安装后在 Claude Code 中输入/debugging确认已加载。
附带文件references/bug-categories.mdreferences/post-mortem-template.md
详细介绍
调试
用于查找和修复错误的系统化方法。优先分析根因,而非处理症状;以证据为依据,而非凭直觉;重在预防,而非反复补救。
铁律
根因未明,不得修复。 在能够解释错误为何存在(而不仅是出在哪里)之前,绝不要应用修复。只针对症状的修复会引发新错误。
适用场景
- QA 提交的错误报告或生产环境告警
- 原因不明的测试失败
- 间歇性/不稳定行为
- 性能下降
- 曾经正常、如今表现异常的行为
- 组件之间的集成失败
工作流
阶段 1:复现
开始调查前,先确保能够稳定复现。
- 收集所有证据:错误消息、堆栈跟踪、日志、屏幕截图、用户操作步骤
- 确定确切条件:环境、数据状态、用户操作、时序
- 逐步去除触发错误所不需要的内容,创建最小复现案例
- 确认复现是否稳定(如果是间歇性的,记录发生频率和条件)
- 精确记录复现步骤,确保他人能够照此操作
产出:已记录的复现步骤、最小测试用例
如果无法复现:记录尝试过的方法,检查环境差异,添加检测埋点,并等待下次发生。不要凭猜测进入阶段 2。无法复现的错误应记录下来,而不是“修复”。
阶段 2:调查
系统地收集证据。此阶段不要形成假设,重点是观察,而非解释。
- 阅读完整的错误消息和堆栈跟踪,逐行查看,不要只看第一行
- 检查近期的 git 历史记录(
git log --since="2 weeks ago"、git bisect) - 从入口点到故障点,追踪数据流
- 检查边界:数据会在哪里跨越组件、服务或层级边界?
- 收集环境信息:版本、配置、依赖项、资源状态
- 确定影响范围:还有哪些地方受到影响?这是孤立故障,还是系统性问题?
生产环境与开发环境中的调试:
- 生产环境:先评估影响并缓解。调查之前,能否缩小影响范围?只能使用只读访问权限,绝不要通过修改生产环境状态来调试。
- 开发环境:你拥有完全控制权。可以自由使用断点、修改状态、添加临时日志。
产出:证据日志(发现的内容、位置、时间戳)、受影响组件图
阶段 3:提出假设
提出多个相互竞争的假设,并按证据强弱排序。
- 列出所有可能的原因。不要只围绕第一个想法展开
- 根据错误类别对每个假设进行分类(参见错误类别参考)
- 评估每项假设的证据强度(强/中/弱)、可测试性(容易/困难)和可能性
- 优先选择可能性最高且最容易测试的假设
- 明确每项假设的验证条件和证伪条件
假设表示例:
| # | 假设 | 类别 | 证据 | 可测试性 | 测试计划 |
|---|---|---|---|---|---|
| 1 | 更新后缓存仍返回旧数据 | 状态 | 日志显示写入后 2 秒仍为旧值 | 容易 | 绕过缓存并进行对比 |
| 2 | 两个工作进程之间存在竞态条件 | 竞态条件 | 间歇性发生,与高负载相关 | 中等 | 添加锁并进行压力测试 |
| 3 | 上游 API 返回了意外格式 | 集成 | 暂无证据 | 容易 | 记录原始响应 |
产出:按证据排序的假设列表及测试计划
阶段 4:测试
每次验证一个假设。每次测试只更改一个变量。
- 更改一项内容并观察结果
- 如果得到证实,继续进入阶段 5
- 如果被证伪,更新证据日志并转向下一个假设
- 如果无法得出结论,增加检测手段并收集更多证据
- 如果连续 3 个假设都失败,立即停止并重新审视你的假设。问题模型可能有误。
危险信号(立即返回阶段 2):
- “先快速修复,之后再调查”
- 一次更改多项内容
- 未弄清问题就进行修复
- 不理解原理就从网上复制粘贴修复方案
输出:有证据链支持的已确认根本原因
阶段 5:修复
在源头修复,而不是修复症状。
- 先编写一个能重现该错误的失败测试
- 实施单一、聚焦于解决根本原因的更改
- 验证失败测试现在能够通过
- 运行完整测试套件以发现回归问题
- 审查自己的修复:这是最简单且正确的解决方案吗?
修复原则:
- 在错误数据/状态产生的源头修复,而不是在错误出现的位置修复
- 增加纵深防御:即使修复了源头,也要在边界处进行验证
- 相较于运行时验证,优先采用让无效状态无法表示的设计
- 一个错误 = 一个修复 = 一个提交 = 一个测试
输出:包含回归测试的修复方案,以及干净通过的测试套件
阶段 6:预防
确保此类错误不会再次发生。
- 在错误数据进入的位置增加防御性验证
- 改进错误消息。六个月后,你能否立即看懂这个错误?
- 如果这是生产环境问题,更新监控/告警
- 如果错误影响重大,撰写事后复盘(参见事后复盘模板)
- 与团队分享发现,这是积累组织知识的方式
输出:预防措施;如影响重大,则撰写事后复盘
错误类别应对策略
不同类型的错误需要不同的调查方法。完整指南请参见错误类别参考。
| 类别 | 首要步骤 | 关键技巧 |
|---|---|---|
| 逻辑错误 | 阅读代码,追踪条件判断 | 橡皮鸭式逐步讲解、真值表 |
| 数据问题 | 检查各边界处的实际数据与预期数据 | 边界日志、数据流追踪 |
| 状态/竞态条件 | 为所有状态变更添加时间戳 | 时序图、并发分析 |
| 集成失败 | 检查是否符合 API 契约 | 请求/响应日志、契约测试 |
| 性能问题 | 先进行性能分析,避免猜测 | 性能分析器、火焰图、查询分析 |
| 环境问题 | 比较正常与异常的环境 | 差异分析、配置审查 |
| 间歇性/不稳定问题 | 优先提升可观测性 | 统计日志、压力测试 |
升级处理标准
在以下情况下,停止调试并升级处理:
- 花费的时间超过最初估算的 2 倍,却没有实质性进展
- 修复需要进行超出你所负责组件范围的架构更改
- 根本原因位于你无法控制的依赖项中
- 在同一区域发现了 3 个或更多错误,这意味着代码需要重新设计,而不是继续打补丁
- 该错误暴露了根本性的设计缺陷
- 生产环境影响正在扩大,采用临时方案或回滚比修复更快
升级给:
| 情况 | 升级给 |
|---|---|
| 设计或架构问题 | 架构师 |
| 无法复现,需要更多信息 | QA 团队 |
| 范围、优先级或权衡问题 | PM / 产品负责人 |
| 依赖项或基础设施问题 | 平台 / DevOps 团队 |
| 发现安全影响 | 立即告知安全团队 |
决策框架
修复深度
- 在错误数据/状态产生的源头修复,而不是在错误出现的位置修复
- 增加纵深防御:即使修复了源头,也要在边界处进行验证
- 相较于运行时验证,优先采用让无效状态无法表示的设计
修复范围
- 修复特定的 Bug,而不是周边代码
- 如果发现附近还有其他问题,请单独提交。不要扩大 Bug 修复的范围
- 一个 Bug = 一项修复 = 一个提交 = 一个测试
何时重写,何时打补丁
- 打补丁:孤立的 Bug,根本原因明确,代码其他部分没有问题
- 重写:同一模块中有 3 个或更多 Bug,根本原因在于结构,且修复比重写更复杂
- 回滚:生产环境故障严重,而之前的版本可以正常工作。先回滚,再排查
与团队角色协作
此调试工作流与更广泛的团队流程相衔接:
| 阶段 | 团队协作 |
|---|---|
| 复现 | QA 提供包含复现步骤的 Bug 报告;如果信息不足,请求补充细节 |
| 调查 | 架构师可以帮助梳理组件依赖关系和影响范围 |
| 修复 | 合并前由同事审查代码,因为多一双眼睛能发现修复引入的回归问题 |
| 预防 | 与团队分享事后分析;在待办事项中跟踪行动项 |
使用此知识库中的其他技能时:
| 情况 | 推荐技能 |
|---|---|
| 修复 Bug 时发现设计问题 | design-patterns |
| 修复涉及重构 | refactoring |
| SOLID 违规是根本原因 | solid |
| 为修复创建 PR | pr-message-writer |
质量检查清单
标记 Bug 修复完成前:
- 已找出并记录根本原因
- 修复前已有失败测试
- 修复针对根本原因,而非表面症状
- 完整测试套件通过
- 修复方案是最简单且正确的方案
- 在适用情况下改进了错误消息
- 针对重大 Bug 撰写了事后分析
- 如果 Bug 影响共享组件,已通知团队
关键规则
-
没有根本原因,就不修复。 这是铁律。如果无法解释 Bug 为何存在,就说明调查尚未完成。
-
先复现。 不要调查无法复现的问题。如果无法复现,就增加可观测性并等待。
-
单变量测试。 测试假设时一次只更改一项内容。更改多个变量会导致结果无法解读。
-
证据优于直觉。 记录证据。“我觉得可能是 X”不是假设。“日志行 Y 显示值为 Z,但预期应为 W”才是。
-
前后都要测试。 没有回归测试的修复迟早会再次出问题。
-
及时求助,不要碍于面子。 懂得何时停下来寻求帮助也是一种能力。请参阅上文的升级处理标准。
-
为后来者留下文档。 六个月后,排查这一区域的人可能还是你自己。让代码库的可观测性比你接手时更好。
-
绝不要通过修改生产环境来调试生产问题。 仅以只读方式调查。修复必须经过常规部署流程。
-
严格控制范围。 修复 Bug,只修复这个 Bug。其他改进应单独创建工单。
-
分享所学。 每个重大 Bug 都是团队学习的机会。事后分析不是为了追责,而是为了积累组织经验。