Claude Code 调试实战:如何正确喂上下文让 AI 精准定位 Bug

发布时间:2026/10/9 22:30:27
Claude Code 调试实战:如何正确喂上下文让 AI 精准定位 Bug 1. 为什么调试场景下我更愿意把问题交给 Claude Code很多人第一次接触 Claude Code是把它当成一个代码生成器——给个需求让它写个函数、补个测试、生成一段脚本。用了一阵子之后大部分人会形成两种印象要么觉得它挺神要么觉得它写的东西看着对、跑起来错。但真正让我把它留在日常工作流里的其实不是写代码而是找问题。调试这件事本质上和写新代码是两种完全不同的脑力活动。写代码是从零到一你脑子里有一个目标状态然后一步步搭出来调试是从一到零你面对的是一个已经存在的、行为不符合预期的系统你要做的是一层层剥开它找到那个本来不该这样的地方。前者考验的是构建能力后者考验的是怀疑能力——你得怀疑自己的假设、怀疑日志、怀疑这段代码肯定没问题的直觉。Claude Code 在调试场景下的价值恰恰在于它没有这段代码是我写的所以肯定没问题这种心理包袱。它读代码的方式是纯文本的、逐行的、不带情绪的。你给它一个报错栈、一段异常日志、一个明明应该返回 A 却返回了 B的现象描述它会老老实实从代码里找证据而不是像人一样先入为主地跳过某些看起来没问题的行。我自己的使用习惯是这样的写新功能的时候我更多是自己动手Claude Code 帮我补样板、查 API 用法但一旦进入这个东西不对劲的状态我会第一时间把上下文丢给它。原因很简单——人在调试时最容易陷入思维定势而它不会。你盯着一个 bug 看两个小时脑子里已经形成了问题一定在 X 处的执念这时候让一个没有执念的东西重新读一遍代码往往能一句话点醒你。这篇文章我想聊的不是Claude Code 有多强这种空话而是把它当成一个调试工具来用的时候具体该怎么喂上下文、怎么提问、怎么验证它的结论、以及哪些坑我踩过。适合已经用过它写代码、但还没把它当调试助手用起来的开发者也适合那些知道它能读代码但不知道怎么问的人。2. 调试和写代码是两种提问方式喂错上下文等于白问2.1 写代码时你给的是目标调试时你给的是现象这是最核心的一个认知转变。你让 Claude Code 写一个函数你会说帮我写一个解析 CSV 的函数支持引号转义。这是一个目标描述它据此生成代码。但调试的时候如果你还是用这种目标式提问比如帮我看看这个函数为什么错了它只能靠猜。正确的做法是给它现象而不是给它判断。这个函数错了是你的判断不是现象。现象应该是输入是a,b,c,d期望解析出三个字段实际解析出四个字段第三个字段是c第四个是d。 这个描述里没有任何我觉得问题在哪的暗示全是可观测的事实。Claude Code 拿到这种描述会自己去代码里找引号处理的逻辑而不是顺着你的判断去验证一个可能错误的假设。我踩过的一个典型坑有一次一个接口返回 500我直接跟它说我觉得是数据库连接池的问题帮我看看。结果它真的顺着连接池的方向分析了一大堆最后发现根本不是——是一个空指针。后来我改成只描述现象这个接口在并发 10 以上时偶发 500日志里最后一行是 XXX单次请求正常。 它立刻就定位到了并发场景下的共享状态问题。提示提问时把我觉得应该是肯定是这类词全部删掉只留输入是什么、期望是什么、实际是什么、报错是什么。这是调试提问的第一原则。2.2 上下文要给最小可复现集不是给整个项目另一个极端是有人把整个仓库丢给它说帮我找 bug。这在理论上可行但实际效果很差。原因有两个一是上下文太长它的注意力会被大量无关代码稀释二是它不知道哪条路径是真正被触发的只能靠静态分析猜调用链。我的做法是给一个最小可复现集触发问题的那个入口函数、它直接调用的几个关键函数、相关的数据结构定义、以及完整的报错信息。通常控制在几百行以内。如果问题涉及跨模块调用我会把调用链上每一层的函数签名和关键实现贴出来但把无关的分支删掉。举个例子一个典型的调试上下文长这样【现象】 调用 processOrder(orderId) 时抛出 TypeError: Cannot read property price of undefined orderId ORD-123 【期望】 返回订单总价 【实际】 在 calculateTotal 第 47 行崩溃 【相关代码】 // order.js function processOrder(orderId) { const order getOrder(orderId); return calculateTotal(order.items); } // calc.js function calculateTotal(items) { return items.reduce((sum, item) sum item.price * item.qty, 0); } // 数据样例 { id: ORD-123, items: [{ sku: A1, qty: 2 }] }注意最后那个数据样例——这是很多人会漏掉的关键信息。光看代码item.price看起来没问题但一看数据样例items里的对象根本没有price字段。Claude Code 拿到这个上下文几乎不需要推理就能指出问题。而如果你只贴代码不贴数据它可能会怀疑items是 undefined、怀疑reduce用法、怀疑一堆无关的东西。2.3 报错栈要贴全但日志要筛过报错栈stack trace我建议原样贴全包括那些看起来是框架内部的帧。因为框架帧里往往藏着关键信息——比如是哪个中间件、哪次重试、哪个序列化环节触发的。Claude Code 对常见框架的调用栈很熟悉它能从帧的顺序判断出执行路径。但日志不一样。日志要筛。一个请求的完整日志可能有几百行其中大部分是正常的 INFO 级别输出。你要做的是把报错前后的 ERROR、WARN以及和这个请求相关的关键 INFO 挑出来按时间顺序贴。如果你不确定哪些相关可以贴报错时间点前后各 20 行但不要贴整个日志文件。我一般的筛选标准是报错行本身、报错前最后一次状态变更的日志、报错后框架的兜底处理日志。这三段基本能还原出崩溃前发生了什么。3. 让 Claude Code 复现问题比让它直接给答案更有价值3.1 先让它讲一遍代码在做什么再让它找问题这是我用得最多的一个技巧也是我觉得最被低估的用法。当你把一段代码丢给 Claude Code 时不要上来就问哪里错了而是先问请逐行解释这段代码的执行流程包括每个变量的值在每一步是什么。这个动作的价值在于它会把你脑子里的我以为这段代码在做什么和代码实际在做什么对齐。很多时候 bug 不是代码写错了而是你对代码的理解错了。当它逐行讲出来你会在某一句话上突然意识到等等这里我以为它是 A原来它是 B——问题就找到了。我遇到过一个特别典型的例子。一段状态机代码我坚信某个状态转换不会发生结果让它逐行讲了一遍它讲到某一行时说此时 flag 为 true因此进入这个分支状态被重置为初始值。我一看那个 flag 确实在这个路径下会是 true只是我一直没意识到。整个过程它没有找 bug只是讲代码但 bug 自己浮出来了。3.2 让它写一个最小复现脚本如果问题涉及运行时行为光看代码不够可以让它写一个最小复现脚本。比如根据上面的代码和数据写一个 Node 脚本能稳定复现这个 TypeError。 它会构造出调用链和数据你跑一下如果复现了说明你给的上下文是完整的如果没复现说明还有隐藏条件没给出来——这本身就是重要线索。这个技巧的妙处在于它把调试变成了验证上下文完整性。很多时候我们以为给全了信息其实漏了某个环境变量、某个初始化顺序、某个全局状态。复现脚本跑不通就是在提醒你还有东西没交代。3.3 用假设-验证的方式逼它给出可检验的结论Claude Code 有时候会给出比较模糊的结论比如可能是这里的数据类型不对。这种结论没法直接验证。这时候你要追问请给出一个具体的验证方法能证明或证伪这个假设。比如它说可能是 items 为空数组你就追问那我加一行console.log(items.length)应该看到什么如果看到 0 说明什么如果看到 3 说明什么 它会给你一个明确的判断标准。然后你去跑用结果反推。这种假设-验证的循环比它直接给一个答案可靠得多因为答案可能是错的但验证方法是可执行的。我一般会要求它把假设列成表格每条假设配上如果成立应该观察到什么和验证方式。这样我一条条跑很快就能排除掉大部分。假设若成立应观察到验证方式items 为 undefined报错在 reduce 调用处而非 item.price在 calculateTotal 入口打印 itemsitem.price 字段名不对打印 item 对象能看到实际字段名打印 items[0] 的 keysqty 为字符串总价出现字符串拼接而非数值相加打印 typeof item.qty这张表是我实际调试时真的会做的不是摆样子。跑完三条基本就能锁定方向。4. 几个真实调试场景里我是怎么用它的4.1 场景一异步竞态导致的偶发数据错乱有个功能是页面加载时并发请求两个接口然后合并结果渲染。问题是偶尔会渲染出旧数据。这种偶发问题最难查因为本地跑十次可能只出现一次。我把两个请求函数、合并函数、以及渲染函数贴给它描述了现象并发请求 A 和 B合并后渲染。偶发渲染出上一次的数据。 它看完之后指出合并函数里用了模块级的缓存变量而两个请求的回调没有保证顺序如果 B 先返回、A 后返回缓存会被 A 覆盖但渲染用的是 B 触发的那次。这个分析的关键在于它注意到了模块级变量这个共享状态。我自己看的时候注意力全在请求和渲染上完全没往缓存变量那边想。它给的建议是把缓存改成请求级别的局部变量或者用请求 ID 做校验。我选了后者因为改动小。注意涉及并发、异步、共享状态的问题一定要把哪些变量是跨调用共享的明确标出来。Claude Code 对共享状态的敏感度很高但你得先让它看见。4.2 场景二配置合并顺序引发的神秘覆盖一个服务的配置来自三层默认配置、环境配置、运行时覆盖。问题是某个运行时覆盖不生效。我把三层配置的样例和合并函数贴出来它一眼看出合并函数用的是浅合并Object.assign 那种而运行时覆盖里有个嵌套对象浅合并会把整个嵌套对象替换掉而不是深度合并。这个问题的隐蔽性在于单看每一层配置都对合并函数也对但组合起来行为不符合预期。Claude Code 的优势是它能同时看到三层数据和合并逻辑然后指出浅合并 嵌套对象这个组合的问题。人看的时候容易分层看反而看不出组合问题。我后来让它给了一个深度合并的实现并且加了一个测试用例覆盖这个场景。这里有个经验让它修 bug 的时候顺手让它补一个能复现这个 bug 的测试。这样下次有人改合并逻辑测试会拦住。4.3 场景三第三方库版本不匹配的隐性报错有一次升级了一个依赖之后某个功能开始报一个很奇怪的错错误信息里全是库内部的符号。我把 package.json 里的版本、报错栈、以及调用这个库的那几行代码贴给它。它对比了报错栈里的函数名和当前版本的 API指出报错栈里的某个函数在当前版本已经改名了说明实际加载的版本和 package.json 里写的不一致——很可能是 lock 文件没更新或者有另一个依赖锁了旧版本。这个判断我一开始完全没想到因为我一直以为package.json 写了新版本就是新版本。它提醒我去看 lock 文件和依赖树果然发现有个间接依赖锁了旧版。这类问题靠读业务代码是永远找不到的必须结合依赖信息。4.4 场景四边界条件遗漏导致的越界一个数组处理的函数正常数据没问题遇到空数组或单元素数组就崩。我把函数和几组测试数据贴出来包括正常、空、单元素三种。它指出循环里用了arr[i1]但没有对最后一个元素做边界判断空数组时arr[0]也是 undefined。这种问题其实不难但人在写的时候容易只想着正常情况。Claude Code 的好处是它会系统性地检查边界——空、单元素、最大、负数、null。我后来养成了一个习惯写完任何处理数组或集合的函数都让它列一遍边界情况比我自己想全。5. 它给的结论不一定对验证这一步不能省5.1 最常见的错误它脑补了不存在的代码Claude Code 有个毛病就是当上下文不完整时它会根据常见模式脑补出一些代码然后基于脑补的内容给结论。比如你只贴了调用方没贴被调方它可能会假设被调方是某种常见实现然后说问题在被调方没有做空值检查——但实际被调方可能做了。识别这种脑补的方法是看它的结论里有没有引用你没提供的代码。如果它说第 30 行的判断有问题而你根本没贴第 30 行那它就是在脑补。这时候你要么补上那段代码要么直接问它你提到的第 30 行是我没提供的你是基于什么假设5.2 它的修复方案可能引入新问题它给的修复方案尤其是涉及并发、内存、性能的一定要自己审一遍。我遇到过一次它建议用一个全局 Map 做缓存来优化重复计算但那个场景下 key 的构造有歧义会导致不同输入命中同一缓存。这种问题它不会主动提醒因为它关注的是解决当前 bug不是不引入新 bug。我的做法是拿到修复方案后问它一句这个改动可能引入哪些新的风险 它通常能列出来。然后我针对这些风险再决定要不要采纳。5.3 用反例测试验证它的结论如果它说问题在 X你可以构造一个如果 X 是原因那么改掉 X 后问题应该消失的测试。改掉 X跑一遍。如果问题还在说明它的结论错了或者不是唯一原因。这个动作花不了几分钟但能避免你顺着错误方向改半天。我一般会要求它同时给出验证这个结论的最小改动。比如它说是空值问题那就加一行空值判断看是否解决。如果解决再考虑是加判断还是从源头修。6. 把调试经验沉淀下来下次不用从头问6.1 让它把排查过程整理成文档每次调试完我会让它把现象、排查路径、根因、修复、验证方式整理成一段 markdown。这段文档我会放进项目的docs/debug/目录。下次遇到类似问题直接搜关键词就能找到。这比在聊天记录里翻要高效得多。更重要的是整理的过程本身会暴露我其实没完全搞懂的地方。如果它整理出来的根因我看了觉得好像对又好像不对说明我还没真正理解得再追问。6.2 把高频问题做成检查清单调试多了会发现很多 bug 是同一类空值、边界、并发、类型、配置覆盖、版本不匹配。我让 Claude Code 帮我把这些整理成一个上线前自查清单每次发版前过一遍。清单不长但能拦住大部分低级问题。这个清单我贴一部分出来你可以参考所有外部输入是否做了空值和类型校验所有数组/集合操作是否考虑了空、单元素、超长所有共享状态是否考虑了并发访问所有配置合并是否明确了浅合并还是深合并所有依赖升级是否检查了 lock 文件和间接依赖所有异步回调是否处理了错误分支6.3 把提问模板固定下来最后分享一个我自己的习惯我把调试提问的模板固定成了几段——现象、期望、实际、上下文、已尝试。每次提问前先填这个模板填不出来的地方就是我还需要补充的信息。这个模板逼着我把问题描述清楚而描述清楚的过程往往问题就解决了一半。模板大概长这样【现象】 【期望行为】 【实际行为】 【报错信息】 【相关代码】最小集 【数据样例】 【已尝试的排查】用久了之后我发现很多时候我填到数据样例那一栏就卡住了——因为我根本没准备好一份能复现的数据。这时候我就知道我还没到能问问题的阶段得先去把数据准备好。这个卡住本身就是价值。调试这件事工具能帮你的前提是你把问题描述清楚。Claude Code 是个很好的调试伙伴但它不是读心术。你给它的上下文越接近可复现的最小集它给的结论就越靠谱。反过来如果你只是丢一句这个不对那它也只能陪你猜。我用了这么久最大的体会就是它逼着我把问题想清楚而想清楚问题bug 就已经解决一半了。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询