issue-troubleshooting
问题排查与修复
概述
随机猜测式修 bug 浪费时间,还会引入新问题。快速补丁掩盖根因,迟早复发。
核心原则:先找到根因,再动手修复。治症不治本就是失败。
本 skill 在经典调试流程基础上,新增修复规模评估环节——当修复工作量较大时,
联动 implementation-planning skill 创建结构化修复计划,避免盲目修改导致混乱。
铁律
没有根因调查,就不允许提修复方案
未完成 Phase 1 之前,不能提出任何修复建议。
适用场景
适用于任何技术问题:
- 测试失败
- 生产 bug
- 异常行为
- 性能问题
- 构建失败
- 集成问题
尤其适用于:
- 有时间压力时(紧急情况更易猜测)
- "看上去很简单,改一下就行"时
- 已经试了多次修复都没用时
- 上一次修复没生效时
- 没有完全理解问题时
不应跳过的场景:
- 问题看起来简单(简单 bug 也有根因)
- 着急(匆忙等于返工)
- 领导催着修复(系统排查比瞎改更快)
五阶段流程
每个阶段必须完成后才能进入下一阶段。
Phase 1:根因调查
在提出任何修复之前:
-
仔细阅读错误信息
- 不要跳过任何错误或警告
- 错误信息往往包含精确线索
- 完整阅读堆栈跟踪
- 记录行号、文件路径、错误码
-
稳定复现
- 能可靠触发吗?
- 精确的复现步骤是什么?
- 每次都出现吗?
- 无法复现 → 收集更多数据,不要猜
-
检查近期变更
- 什么变更可能导致了这个问题?
- Git diff、近期提交
- 新依赖、配置变更
- 环境差异
-
多组件系统收集证据
当系统有多个组件(CI → 构建 → 签名,API → 服务 → 数据库):
在提出修复之前,添加诊断埋点:
对每个组件边界: - 记录进入组件的数据 - 记录离开组件的数据 - 验证环境/配置传播 - 检查每层状态 运行一次收集证据,定位故障出现在哪一层 再针对该组件深入调查 -
追踪数据流
当错误在调用栈深处:
参见
references/root-cause-tracing.md的完整回溯追踪技术。简版:
- 错误值从哪来?
- 谁传入了错误值?
- 持续向上追溯直到源头
- 在源头修复,不在表象处修复
不确定是哪个测试造成污染时: 使用
scripts/find-polluter.sh二分定位:./scripts/find-polluter.sh '.git' 'src/**/*.test.ts'
Phase 2:模式分析
修复前先找模式:
-
找正常工作的参照
- 在同一代码库找到类似的正常工作代码
- 什么能正常工作?跟出问题的有什么相似?
-
对比参考实现
- 如果是在实现某个模式,完整阅读参考实现
- 不要略读——逐行阅读
- 完全理解模式后再应用
-
识别差异
- 正常代码和异常代码之间有什么不同?
- 列出每个差异,不管多小
- 不要假定"这不重要"
-
理解依赖
- 需要哪些其他组件?
- 需要什么设置、配置、环境?
- 有哪些隐含假设?
Phase 3:假设验证
科学方法:
-
提出单一假设
- 明确陈述:"我认为 X 是根因,因为 Y"
- 写下来
- 要具体,不要含糊
-
最小化测试
- 做最小改动来验证假设
- 一次只改一个变量
- 不要同时修多个东西
-
验证后再继续
- 验证通过?→ 进入 Phase 4 规模评估
- 没通过?→ 提出新假设
- 不要在失败的假设上叠加新修改
-
不懂就说不懂
- 说"我不理解 X"
- 不要装懂
- 寻求帮助
- 继续研究
Phase 4:修复规模评估
在进入实施之前,评估修复的规模和复杂度。这是本 skill 区别于普通调试流程的关键步骤。
规模评估标准
对已确认的根因,评估以下维度:
| 维度 | 小修 | 大修 |
|---|---|---|
| 涉及文件数 | ≤ 3 个文件 | > 3 个文件 |
| 涉及模块 | 单一模块 | 跨模块/跨层 |
| 预估工时 | < 4 小时 | ≥ 4 小时 |
| 变更性质 | 逻辑修正/参数调整 | 架构调整/接口变更/新增组件 |
| 测试影响 | 修改少量测试 | 需要新增测试套件/重构测试结构 |
| 前后端 | 仅一端 | 前后端都需改动 |
决策规则
小修(满足以下全部条件)→ 直接进入 Phase 5:
- 修改 ≤ 3 个文件
- 不跨模块
- 预估 < 4 小时
- 不涉及接口变更
大修(满足以下任一条件)→ 联动 implementation-planning:
- 修改 > 3 个文件
- 跨模块或跨层
- 预估 ≥ 4 小时
- 涉及接口变更、架构调整
- 前后端都需改动
- Phase 3 中已失败 3+ 次假设(说明问题可能在架构层面)
联动 implementation-planning 的流程
当判断为大修时:
-
整理修复方案概要
- 根因说明(来自 Phase 1-3 的结论)
- 修复范围(涉及的模块/层/文件)
- 修复策略(来自 Phase 2 的模式分析)
- 修复约束(不能破坏的现有行为、兼容性要求)
-
检查技术方案是否已存在
- 如果
workplace/1.X/tech-design/下有相关技术方案 → 直接引用 - 如果没有 → 先输出一份简要修复方案(作为技术方案的替代),再进入实施计划
- 如果
-
调用 implementation-planning skill
- 将修复方案概要作为输入
- 生成的计划中每个模块标注
[BUGFIX]前缀,区分于正常需求开发 - 计划中增加"回归验证"模块作为最后一个模块
-
修复计划模板补充
联动生成的计划中,每个模块详情需额外包含:
**关联根因**:[本模块修复的根因部分] **风险点**:[本模块修改可能影响的现有行为] **回退方案**:[如果修复引入新问题如何回退]计划末尾增加回归验证模块:
### M{N}: 回归验证 **目标**:确认修复未引入新问题 **层**:跨层 **前置依赖**:所有修复模块 **子步骤**: 1. 运行全量测试套件 2. 验证原 bug 场景已修复 3. 检查关联功能的回归 **验收标准**: - 全量测试 PASS - 原 bug 复现步骤不再触发 - 关联功能无回归 -
向用户说明
- 告知用户为什么判定为大修
- 展示修复方案概要
- 提交计划供用户确认后再执行
Phase 5:修复实施
根据 Phase 4 的决策,走两条路线:
路线 A:小修直接实施
-
创建失败测试用例
- 最简复现
- 尽量自动化测试
- 无框架时用一次性脚本
- 修复前必须有测试
-
实施单一修复
- 针对已确认的根因
- 一次改一处
- 不做"顺手优化"
- 不捆绑重构
-
验证修复
- 测试通过了?
- 没有破坏其他测试?
- 问题确实解决了?
-
修复未生效
- 停下来
- 计数:已经试了几次修复?
- < 3 次 → 回到 Phase 1,用新信息重新分析
- ≥ 3 次 → 进入架构质疑(步骤 5)
- 不要在 3 次失败后继续试第 4 次
-
3+ 次修复失败:质疑架构
架构问题的信号:
- 每次修复都在不同位置暴露新的共享状态/耦合
- 修复需要"大规模重构"才能实施
- 每次修好在 A 处,B 处又出问题
停下来质疑根本问题:
- 这个模式本身是否合理?
- 是不是在靠惯性坚持?
- 应该重构架构还是继续修补?
在尝试更多修复前,与用户讨论。
这不是假设失败——这是架构选错了。
→ 此时应联动
implementation-planning,将架构重构作为正式计划推进。
路线 B:大修按计划执行
按 implementation-planning 生成的修复计划执行:
- 读取计划中的执行索引,找到第一个"待执行"模块
- 按模块详情实施
- 完成后更新状态为"完成"
- 继续下一个模块
- 最后执行回归验证模块
红灯信号——停下来遵循流程
当你发现自己在想:
- "先快速修一下,回头再查"
- "改一下 X 试试看"
- "把几个改动一起提交"
- "跳过测试,我手动验证"
- "大概是 X 的问题,我先改了"
- "不完全理解但这样可能行"
- "参考文档太长,我按自己的方式来"
- 还没追踪数据流就提出解决方案
- "再试一次修复"(已经失败 2+ 次)
- 每次修复都在不同位置暴露新问题
以上所有都意味着:停下来,回到 Phase 1。
3+ 次修复失败:质疑架构(见 Phase 5 路线 A 步骤 5)
来自用户的纠偏信号
注意这些提示:
- "是这样吗?" → 你假设了但没验证
- "能不能看到……?" → 应该先加证据收集
- "别猜了" → 你在没有理解的情况下提出修复
- "好好想想" → 质疑根本问题,不是表象
- "是不是卡住了?" → 你的方法不奏效
看到这些信号时:停下来,回到 Phase 1。
常见借口
| 借口 | 事实 |
|---|---|
| "问题很简单,不需要流程" | 简单问题也有根因。流程对简单 bug 更快。 |
| "紧急,没时间走流程" | 系统排查比瞎猜快。 |
| "先试试这个,不行再查" | 第一次修复定基调。从头就做对。 |
| "先改了确认有效再补测试" | 没测试的修复靠不住。测试先证明问题存在。 |
| "几个改动一起省时间" | 无法隔离哪个有效。还会引入新 bug。 |
| "参考太长,我按自己理解来" | 一知半解必定出 bug。完整阅读。 |
| "看到问题了,我来改" | 看到症状 ≠ 理解根因。 |
| "再试一次修复"(失败 2+ 次后) | 3+ 次失败 = 架构问题。质疑模式,不要继续试。 |
| "大修就直接动手改吧" | 大修没有计划必乱。用 implementation-planning 管住范围。 |
快速参考
| 阶段 | 关键活动 | 成功标准 |
|---|---|---|
| 1. 根因调查 | 读错误、复现、查变更、收证据 | 理解"是什么"和"为什么" |
| 2. 模式分析 | 找参照、对比差异 | 识别关键差异 |
| 3. 假设验证 | 提出假设、最小测试 | 假设确认或提出新假设 |
| 4. 规模评估 | 评估修复范围和复杂度 | 判定小修直接修 / 大修走计划 |
| 5. 修复实施 | 小修:测试→修复→验证 / 大修:按计划执行 | bug 解决,测试通过 |
当流程揭示"找不到根因"
如果系统调查后发现问题是环境、时序或外部因素导致的:
- 你已经完成了流程
- 记录调查了什么
- 实现适当的处理(重试、超时、错误提示)
- 添加监控/日志供未来调查
但注意: 95% 的"找不到根因"其实是调查不够深入。
辅助技术
以下技术是本 skill 的一部分,位于 references/ 目录:
root-cause-tracing.md- 沿调用栈回溯追踪 bug 到原始触发点(配套脚本scripts/find-polluter.sh)defense-in-depth.md- 找到根因后在多个层添加验证condition-based-waiting.md- 用条件轮询替代任意超时(配套实现见scripts/condition-based-waiting-example.ts)
关联 skill
implementation-planning- 修复规模大时,创建结构化修复计划test-driven-development- 创建失败测试用例(Phase 5 步骤 1)verification-before-completion- 验证修复生效后再宣布完成