周四的调试时刻
周一写功能,周二修bug,周三上线后问题爆发。
到了周四,你已经盯着那个bug看了整整一天。日志翻了好几遍,断点下了十几个,代码看了又看——但就是找不到根因。
你开始怀疑人生:这代码明明逻辑是对的,为什么跑起来就不对?
这时候你需要的不是多看几遍代码,而是另一双眼睛。
这个模板的作用就是:把你的错误信息、日志片段和代码丢给AI,让它扮演一个资深的调试专家,帮你快速定位问题根因,而不只是告诉你"可能是什么原因"。
核心模板:AI调试助手
把下面这段复制到 Claude 或 ChatGPT(推荐 Claude,对错误堆栈的理解更精准):
你是一位有10年以上经验的资深工程师,擅长debugging和故障排查。你的风格是:先找根因,再给方案;不猜谜,只看证据。
【问题现象】
[用一两句话描述:这个功能期望做什么,实际发生了什么]
【错误信息 / 日志】
[粘贴完整的错误堆栈、异常信息、或关键日志片段。如果没有错误信息,描述异常表现]
【相关代码】
```[语言]
[粘贴你认为可能有问题的代码片段,不要贴整个文件,聚焦在50-100行以内]
【上下文信息】
- 这段代码的作用:[一句话]
- 何时触发:[比如:每次请求都报错 / 只在特定条件下 / 随机出现]
- 是否最近改动过:[是/否,如果选了"是",最近改了什么]
- 测试环境还是线上:[dev / staging / production]
【你已经尝试过的排查】 [你看过哪些日志、加过哪些断点、改过哪些地方。避免AI重复告诉你这些]
请帮我:
第一步:根因分析
- 根据错误信息/日志,推断最可能的根因(1-3个候选)
- 每个候选的支撑证据是什么
- 哪个可能性最高,为什么
第二步:定位方法
- 如果根因推断正确,问题代码大概在哪一行/哪个函数
- 我应该在哪个位置加日志或断点来验证
- 有没有一两个快速验证的方法(不需要部署就能测的)
第三步:修复方案
- 给出修复后的代码(完整可运行的片段)
- 说明为什么这样修能解决问题
- 有没有更好的写法(如果当前写法有明显缺陷)
第四步:同类问题的预防
- 这个bug的根本原因是什么类型的问题(空指针/并发/边界条件/类型转换…)
- 怎么避免同类问题再出现(代码规范/测试/监控)
---
## 三种高频场景变体
### 变体一:错误堆栈分析(最常用)
线上报错,拿到堆栈不知道哪行有问题。
【问题现象】 用户点击"提交订单"后,页面直接白屏,控制台报500错误。
【错误堆栈】
Error: Cannot read properties of undefined (reading 'map')
at OrderSubmit.vue:47
at Proxy.render (vue.runtime.esm.js:4542)
at flushJobs (vue.runtime.esm.js:684)
Caused by: TypeError: this.order.items is undefined
【相关代码】
// OrderSubmit.vue 第40-55行
const submitOrder = async () => {
const items = this.order.items.map(item => ({
productId: item.id,
quantity: item.qty,
price: item.price
}));
const response = await api.createOrder({ items });
if (response.code === 200) {
this.$router.push('/success');
}
};
【上下文】
- order 从父组件通过props传入
- 正常流程没问题,偶尔触发这个bug
- 是生产环境的问题
【已尝试的排查】
- 在items.map前加了console.log(this.order),发现偶尔是undefined
### 变体二:逻辑bug(行为不符合预期)
没有报错,但结果不对。
【问题现象】 用户积分应该在购买后增加,但实际没有变化。
【相关代码】
# points_service.py
def add_points(user_id: int, amount: int) -> bool:
user = User.query.filter_by(id=user_id).first()
if not user:
return False
old_points = user.points
user.points = old_points + amount
db.session.commit()
return True
def on_purchase_completed(order_id: int):
order = Order.query.get(order_id)
if order.status == 'paid':
add_points(order.user_id, order.points_reward)
print(f"Added {order.points_reward} points to user {order.user_id}")
【日志输出】
Added 100 points to user 12345
【上下文】
- 日志显示add_points被调用了,但用户实际积分没变
- 这个逻辑上线两周了,一直没问题,上周改了订单状态机后开始出现
### 变体三:并发/性能问题(偶发性)
问题不固定复现,时好时坏。
【问题现象】 在高并发场景下,偶尔出现数据不一致——同一个订单被处理了两次。
【相关代码】
// handler.go
func HandleOrder(c *gin.Context) {
orderId := c.Param("id")
// 检查订单是否已处理
if isProcessed(orderId) {
c.JSON(200, gin.H{"status": "already_processed"})
return
}
// 处理订单
processOrder(orderId)
// 标记已处理
markAsProcessed(orderId)
}
【上下文】
- 日流量约5000 QPS
- 大约每1000次请求出现1次重复处理
- 数据库是MySQL,没有用分布式锁
---
## 快速版(5分钟搞定)
赶时间?用这个精简版:
帮我debug这段代码:
【问题】 [一句话描述问题现象]
【代码】
[粘贴代码]
【错误信息】 [粘贴错误或日志]
只告诉我:
- 问题在哪一行
- 为什么会出错
- 怎么修
---
## 使用技巧
**贴完整的错误堆栈,不要只贴一行。** "undefined is not a function"这种信息太泛了。把完整的stack trace丢进去,AI能从调用链里找到真正的根因位置。
**告诉AI你已经排查过什么。** 调试最烦的就是AI告诉你"你检查过日志了吗"。直接说"我已经看过XX日志,排除了YY可能",让AI从更深的层次分析。
**聚焦代码片段,不要贴整个文件。** AI的上下文窗口有限,贴100行核心代码比贴1000行更有效。把和bug无关的代码去掉。
**说明问题的复现条件。** "每次必现"和"偶尔出现"是两种完全不同的debug场景。前者通常是逻辑bug,后者多半是并发或状态问题。
**让AI给快速验证方法。** 在给出完整修复方案之前,先问"有没有不用部署就能快速验证根因的方法"。这能帮你节省大量时间。
**调试完让AI写测试用例。** 修完bug后,让AI基于这个bug场景写一个回归测试。防止下次改动再把同样的坑踩一遍。
---
## 效果示例
**输入(用户反馈的bug):**
```
【问题现象】
用户反馈:连续点击"立即购买"按钮两次,系统创建了两个订单。
【相关代码】
```javascript
// checkout.js
let isSubmitting = false;
const handleBuyNow = async () => {
if (isSubmitting) return;
isSubmitting = true;
try {
const result = await api.createOrder({
productId: currentProduct.id,
quantity: 1
});
router.push(`/order/${result.orderId}`);
} catch (error) {
showError('下单失败,请重试');
} finally {
isSubmitting = false;
}
};
```
【上下文】
- 这个bug是上周新出现的
- 之前点击两次只创建一单,现在能创建两单
- 用户用键盘快速连按两次Enter也能复现
```
**AI 输出:**
**第一步:根因分析**
问题根因:**`isSubmitting` 变量作用域问题导致竞态条件。**
支撑证据:
1. 代码里 `isSubmitting` 是模块级变量(`let isSubmitting = false`),在组件重新渲染或路由跳转后会被重置
2. 用户连按两次Enter能复现——说明按钮的disabled状态没有阻止键盘事件
3. 之前没问题现在有问题——很可能最近改了组件结构,`isSubmitting` 从组件内部移到了模块作用域
最可能的根因:当第一个请求的 `router.push` 触发组件卸载/重建时,`isSubmitting` 被重置为 `false`,此时第二个点击就能通过守卫。
**第二步:定位方法**
- 根因位置:`checkout.js` 第1行 `let isSubmitting = false` 以及第8行 `isSubmitting = true`
- 快速验证:在浏览器控制台执行 `isSubmitting` 查看当前值,然后快速点击按钮两次,观察值的变化时机
**第三步:修复方案**
```javascript
// checkout.js - 修复版
import { ref } from 'vue'; // 或 React 的 useState
// 方案A:用组件状态管理(推荐)
const useCheckout = () => {
const isSubmitting = ref(false);
const handleBuyNow = async () => {
if (isSubmitting.value) return;
isSubmitting.value = true;
try {
const result = await api.createOrder({
productId: currentProduct.id,
quantity: 1
});
router.push(`/order/${result.orderId}`);
} catch (error) {
showError('下单失败,请重试');
} finally {
isSubmitting.value = false;
}
};
return { handleBuyNow, isSubmitting };
};
// 方案B:如果必须在模块级,用更强的守卫
const checkoutState = { isSubmitting: false };
const handleBuyNow = async () => {
if (checkoutState.isSubmitting) return;
checkoutState.isSubmitting = true;
try {
const result = await api.createOrder({ ... });
// 在跳转前重置,防止组件重建后状态丢失
checkoutState.isSubmitting = false;
router.push(`/order/${result.orderId}`);
} catch (error) {
checkoutState.isSubmitting = false;
showError('下单失败,请重试');
}
};
```
**第四步:同类问题的预防**
- 这个bug的类型:**按钮防重复点击守卫的状态管理不当**
- 预防措施:
1. 所有防重复提交的状态必须和组件生命周期绑定,不能用模块级变量
2. 在按钮上同时设置 `@click` 和 `@keydown.enter` 的处理,统一用同一个守卫
3. 后端接口也加幂等性保护(orderToken),即使前端守卫被绕过也不会创建重复订单
4. 写一个 `useLoading` 或 `useDebouncedClick` 的通用hook,避免每个页面自己实现防重复逻辑
---
## 调试 vs Code Review 的区别
| 维度 | Code Review | AI Debugging |
|------|------------|-------------|
| 目的 | 预防bug | 修复现有bug |
| 输入 | 完整代码 | 错误信息 + 代码 |
| 焦点 | 潜在风险 | 根因定位 |
| 风格 | 全面扫描 | 精准打击 |
| 最佳时机 | 提交前 | 问题复现后 |
---
## 什么时候不该用
- **bug还没复现。** 如果问题不能稳定复现,让AI分析只会得到一堆猜测。先找到复现路径。
- **问题在第三方库。** 如果是npm包或框架本身的bug,AI能帮你看issue和workaround,但修不了。先去GitHub提issue。
- **你还没看清楚日志。** AI比你能更快地读日志,但前提是日志里有信息。先确保日志足够详细。
---
## 组合使用技巧
**调试 + 周一任务拆解。** 周一用 `monday-task-breakdown` 把本周要修的关键bug列出来,周四用这个模板逐个击破。
**调试 + 周五知识沉淀。** 修完的bug,让AI帮你生成一条"避坑笔记"存到 `saturday-personal-knowledge-base`。下次遇到同类问题,翻笔记比重新debug快。
**调试 + 周四技术方案评审。** 如果同一个模块反复出bug,说明设计方案有问题。用 `thursday-tech-proposal-review` 重新评审这个模块的设计。
---
## 进阶用法
**日志智能解读。** 把一堆乱糟糟的服务日志丢给AI,让它找出时间线上的关键事件:"XX点在YY服务出现了异常,ZZ服务在XX点+2秒后报错,根因可能在YY"。
**多语言跨服务debug。** 如果bug涉及多个服务(前端→网关→后端→数据库),把各层的日志和代码片段一起丢给AI,让它做跨服务链路分析。
**AI当你的debug结对伙伴。** 不只是让AI给答案,而是让它提问:"你看过XX日志了吗?""这个值在YY时候是什么?"模拟一个资深工程师带你debug的过程。
---
## 写在最后
周四下午,你盯着那个该死的bug已经三个小时了。
关掉IDE,打开这个模板。把错误信息、日志、代码丢进去。
十分钟后,AI告诉你问题在哪一行,为什么会出错,怎么修,以及怎么避免下次再踩这个坑。
**好的debugger不是写得更快的人,而是知道去哪看的人。**
这个模板帮你的,就是帮你找到"去哪看"。
把那堆日志和代码丢进去吧,让AI当你的第二双眼睛。