Files
junhong_cmp_fiber/.claude/skills/systematic-debugging/SKILL.md
huang de9eacd273
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m27s
chore: 新增 systematic-debugging 技能,更新项目开发规范
新增 systematic-debugging Skill(四阶段根因分析流程),在 AGENTS.md 和 CLAUDE.md 中补充触发条件说明。opencode.json 配置同步更新。

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-03-04 11:38:01 +08:00

8.7 KiB
Raw Blame History

name, description
name description
systematic-debugging 遇到任何 bug、异常行为、报错时必须使用。在提出任何修复方案之前强制执行根因分析流程。适用于 API 报错、数据异常、业务逻辑错误、性能问题等所有技术问题。

系统化调试方法论

铁律

没有找到根因,禁止提出任何修复方案。

改之前先搞懂为什么坏了。猜测不是调试,验证假设才是。


什么时候用

所有技术问题都用这个流程

  • API 接口报错4xx / 5xx
  • 业务数据异常(金额不对、状态流转错误)
  • 性能问题(接口慢、数据库慢查询)
  • 异步任务失败Asynq 任务报错/卡住)
  • 构建失败、启动失败

尤其是以下场景

  • 时间紧迫(越急越不能瞎猜)
  • "很简单的问题"(简单问题也有根因)
  • 已经试了一次修复但没解决
  • 不完全理解为什么出问题

四阶段流程

必须按顺序完成每个阶段,不可跳过。

阶段一:根因调查

这是最重要的阶段,占整个调试时间的 60%。没完成本阶段,禁止进入阶段二。

1. 仔细阅读错误信息

  • 完整阅读 stack trace不要跳过
  • 注意行号、文件路径、错误码
  • 很多时候答案就在错误信息里
  • 检查 logs/app.loglogs/access.log 中的上下文

2. 稳定复现

  • 能稳定触发吗?精确的请求参数是什么?
  • 用 curl 或 Postman 复现,记录完整的请求和响应
  • 不能复现 → 收集更多数据检查日志、Redis 状态、数据库记录),不要瞎猜

3. 检查最近改动

  • git diff / git log --oneline -10 看最近改了什么
  • 新加了什么依赖?改了什么配置?改了什么 SQL
  • 对比改动前后的行为差异

4. 逐层诊断(针对本项目架构)

本项目有明确的分层架构,问题一定出在某一层的边界:

请求 → Fiber Middleware → Handler → Service → Store → PostgreSQL/Redis
                ↑              ↑          ↑        ↑           ↑
              认证/限流      参数解析   业务逻辑  SQL/缓存    数据本身

在每个层边界确认数据是否正确

// Handler 层 — 请求进来的参数对不对?
logger.Info("Handler 收到请求",
    zap.Any("params", req),
    zap.String("request_id", requestID),
)

// Service 层 — 传给业务逻辑的数据对不对?
logger.Info("Service 开始处理",
    zap.Uint("user_id", userID),
    zap.Any("input", input),
)

// Store 层 — SQL 查询/写入的数据对不对?
// 开启 GORM Debug 模式查看实际 SQL
db.Debug().Where(...).Find(&result)

// Redis 层 — 缓存的数据对不对?
// 用 redis-cli 直接检查 key 的值
// GET auth:token:{token}
// GET sim:status:{iccid}

跑一次 → 看日志 → 找到断裂的那一层 → 再深入该层排查。

5. 追踪数据流

如果错误深藏在调用链中:

  • 坏数据从哪来的?
  • 谁调用了这个函数,传了什么参数?
  • 一直往上追,直到找到数据变坏的源头
  • 修源头,不修症状

阶段二:模式分析

找到参照物,对比差异。

1. 找能用的参照

项目里有没有类似的、能正常工作的代码?

如果问题在... 参照物在...
Handler 参数解析 其他 Handler 的相同模式
Service 业务逻辑 同模块其他方法的实现
Store SQL 查询 同 Store 文件中类似的查询
Redis 操作 pkg/constants/redis.go 中的 Key 定义
异步任务 internal/task/ 中其他任务处理器
GORM Callback pkg/database/ 中的 callback 实现

2. 逐行对比

完整阅读参考代码,不要跳读。列出每一处差异。

3. 不要假设"这个不重要"

小差异经常是 bug 的根因:

  • 字段标签 gorm:"column:xxx" 拼写不对
  • errors.New() 用了错误的错误码
  • Redis Key 函数参数传反了
  • Context 里的 UserID 没取到(中间件没配)

阶段三:假设和验证

科学方法:一次只验证一个假设。

1. 形成单一假设

明确写下:

"我认为根因是 X因为 Y。验证方法是 Z。"

2. 最小化验证

  • 只改一个地方
  • 一次只验证一个变量
  • 不要同时修多处

3. 验证结果

  • 假设成立 → 进入阶段四
  • 假设不成立 → 回到阶段一,用新信息重新分析
  • 绝对不能在失败的修复上再叠加修复

4. 三次失败 → 停下来

如果连续 3 次假设都不成立:

这不是 bug是架构问题。

  • 停止一切修复尝试
  • 整理已知信息
  • 向用户说明情况,讨论是否需要重构
  • 不要再试第 4 次

阶段四:实施修复

确认根因后,一次性修好。

1. 修根因,不修症状

❌ 症状修复:在 Handler 里加个 if 把坏数据过滤掉
✅ 根因修复:修 Service 层生成坏数据的逻辑

2. 一次只改一个地方

  • 不搞"顺手优化"
  • 不在修 bug 的同时重构代码
  • 修完 bug 就停

3. 验证修复

  • go build ./... 编译通过
  • lsp_diagnostics 无新增错误
  • 用原来复现 bug 的请求再跑一次,确认修好了
  • 用 PostgreSQL MCP 工具检查数据库中的数据状态

4. 清理诊断代码

  • 删除阶段一加的临时诊断日志(除非它们本身就该保留)
  • 确保没有 db.Debug() 残留在代码里

本项目常见调试场景速查

场景 首先检查
API 返回 401 logs/access.log 中该请求的 token → Redis 中 auth:token:{token} 是否存在
API 返回 403 用户类型是什么 → GORM Callback 自动过滤的条件对不对 → middleware.CanManageShop() 的参数
数据查不到 GORM 数据权限过滤有没有生效 → shop_id / enterprise_id 是否正确 → 是否需要 SkipDataPermission
金额/余额不对 乐观锁 version 字段 → RowsAffected 是否为 0 → 并发场景下的锁竞争
状态流转错误 WHERE status = expected 条件更新 → 状态机是否有遗漏的路径
异步任务不执行 Asynq Dashboard → RedisTaskLockKey 有没有残留 → Worker 日志
异步任务重复执行 RedisTaskLockKey 的 TTL → 任务幂等性检查
分佣计算错误 佣金类型(差价/一次性) → 套餐级别的佣金率 → 设备级防重复分佣
套餐激活异常 卡状态 → 实名状态 → 主套餐排队逻辑 → 加油包绑定关系
Redis 缓存不一致 Key 的 TTL → 缓存更新时机 → 是否有手动 Del 清除
微信支付回调失败 签名验证 → 幂等性处理 → 回调 URL 是否可达
GORM 查询慢 db.Debug() 看实际 SQL → 是否 N+1 → 是否缺少索引

红线规则

如果你发现自己在想以下任何一条,立刻停下来,回到阶段一

想法 为什么是错的
"先快速修一下,回头再查" 快速修 = 猜测。猜测 = 浪费时间。
"试试改这个看看行不行" 一次只验证一个假设,不是随机改。
"大概是 X 的问题,我直接改了" "大概"不是根因。先验证再改。
"这个很简单,不用走流程" 简单问题走流程只需要 5 分钟。不走流程可能浪费 2 小时。
"我不完全理解但这应该行" 不理解 = 没找到根因。回阶段一。
"再试一次"(已经失败 2 次) 3 次失败 = 架构问题。停下来讨论。
"同时改这几个地方应该能修好" 改多处 = 无法确认哪个是根因。一次只改一处。

常见借口和真相

借口 真相
"问题很简单,不需要走流程" 简单问题也有根因。走流程对简单问题只花 5 分钟。
"太紧急了,没时间分析" 系统化调试比乱猜快 3-5 倍。越急越要走流程。
"先改了验证一下" 这叫猜测,不叫验证。先确认根因再改。
"我看到问题了,直接修" 看到症状 ≠ 理解根因。症状修复是技术债。
"改了好几个地方,反正能用了" 不知道哪个改动修的,下次还会出问题。

快速参考

阶段 核心动作 完成标准
一、根因调查 读错误日志、复现、检查改动、逐层诊断、追踪数据流 能说清楚"因为 X 所以 Y"
二、模式分析 找参照代码、逐行对比、列出差异 知道正确的应该长什么样
三、假设验证 写下假设、最小改动、单变量验证 假设被证实或推翻
四、实施修复 修根因、编译检查、请求验证、清理诊断代码 bug 消失,无新增问题