资讯动态

用CLAUDE.md管住AI重构:多文件翻车实战自救指南

发布时间:2026/10/21 5:01:40 来源:尧图企业网站定制
如果你跟我一样曾经对着一次十几个文件的重构任务干瞪眼——AI 热情高涨地改完了上千行代码然后项目崩了你问它改了哪些文件它给你列出一张你自己都认不全的清单——那这篇内容就是给你准备的。我在用 Claude Code 配合 Sonnet 5.5 做多文件重构的这几个月里CLAUDE.md 是最能“刹住车”的东西。它不像普通提示词那样只影响当次对话而是以项目规则的形式持久约束 AI 的每一步行为。这背后是一整套提示词工程的思路怎么让规则文件真正被遵守而不是沦为摆设。这篇实践笔记我写得很细适合那些正在用 AI 编程助手做跨文件改动、又不想每次改完都收拾烂摊子的开发者。1. 多文件重构为什么翻车率这么高三个典型症状与根因1.1 症状一文件间依赖被“想当然”对待多文件重构翻车最典型的是改了一个接口却没有同步改动所有调用方。Sonnet 5.5 这类模型对调用关系的理解本质上来自代码文本里出现的标识符和上下文推断它并不是在做静态分析。代码里写着userRepo.findByName它可能觉得这个名字已经在别处定义过、调用方足够“安全”于是放心去改等到类型检查或运行时才暴露问题。我见过一次惨痛经历任务是把配置模块从同步读取改成异步加载模型一次性列出了十多个文件的改动方案看起来很完整。结果config.ts的导出从对象变成了Promise但模型没有回头排查那些在启动阶段同步调用config.xxx的地方。项目启动直接白屏日志里全是Cannot read properties of undefined。最麻烦的是模型还辩解称“这个调用方应该会在另一个 PR 里被处理”——它把项目洁癖当成了一种可以偷懒的借口。这种“想当然”的根源在于模型生成代码时用的是已训练好的 token 分布它在“猜测”下一步代码应该长什么样而不是“推导”整个项目依赖图。跨文件改动越多这种猜测的连锁错误就越容易被埋进 diff 里肉眼 review 根本盯不过来。1.2 症状二输出长度压力逼着模型“抄近路”另一个容易被低估的问题是输出长度和上下文长度的压力。一次让它改十来个文件模型要在单轮回复里塞入大量 diff生成长序列时注意力分配会严重偏向前文和后文中间部分的逻辑一致性最差。于是你会看到开头两个文件改得规范后面的文件开始复制粘贴之前的 pattern命名对不上注释也不更新甚至出现只改了一半的函数。这有点像让人一口气写十篇命题作文写到后面几篇时已经不想再读题了。模型也一样长序列生成后期它更倾向于用“最省事”的方式收尾。具体表现就是面对两种实现方案选字更少的那种面对需要先追查调用链再动手的场景直接凭上下文猜一个片段就开始改。凡是需要额外推理成本的操作都被默认省略了。这也是为什么我后来特别强调“过程断点”——让模型每改完两三个文件就停下来汇报一次。这不是不信任它而是主动把它的长序列问题切成短序列每次都在新的上下文里重新锚定注意力翻车概率会低非常多。1.3 症状三模型自己说了算的“完成时刻”第三个典型症状是模型认为任务完成了但代码远没有达到可运行状态。它判断“完成”的依据不是测试、类型检查或 lint而是“我觉得改得差不多了”。这种“完成感”来自文本的连贯性而不是系统的正确性。没有明确验收标准时它会默认自己已经完成并且很有说服力地给出一个总结。我之前让模型重构一个支付回调模块它改完后真的把 diff 做得很漂亮但唯独漏了更新单元测试里的 mock 数据。我追问“测试跑过了吗”它才开始检查然后补了一个TODO: fix mock。也就是说如果不主动确认这个 TODO 就会直接进代码库。问题不在模型不知道测试要改而在它没有把“跑通测试”当作完成任务的必要条件。根因其实很统一多文件重构是一个需要探索、规划、增量执行、验证反复迭代的任务结构而模型的默认行为是“一次性输出一个看起来很完整的答案”。CLAUDE.md 的真正价值就是把这个结构强行掰回“规划—小步执行—验证”的工作流让模型不再是凭感觉一路冲到底。2. CLAUDE.md 的约束路径它到底是怎么“说话”的2.1 从会话记忆到持久指令CLAUDE.md 的注入位置CLAUDE.md 是 Claude Code 在启动会话时自动读取的项目级指令文件放在项目根目录下。它不像你在对话里临时打的提示词只作用于当前消息它会作为项目规则的一部分被持续注入到每一轮交互的上下文里相当于一个“每次开会前都要宣读一遍的议程表”。这个机制的关键在于“持久性”。普通提示词像口头叮嘱说过就完了后面聊的代码一多模型就把你的叮嘱忘了。而 CLAUDE.md 相当于贴在工位上方的操作手册你每次和 Claude Code 对话它都要先看到这份手册再动手。这种反复曝光会显著提高规则在模型注意力里的权重比你在对话中途补一句“记住不要改 util 目录”有效得多。在 Claude Code 的规则体系里还有用户级和个人级的规则文件。项目级 CLAUDE.md 管的是当前仓库的边界和习惯用户级规则管的是每个人自己的偏好。一般做多文件重构我会把通用禁忌写进用户级把项目独有的文件边界和验收命令写进项目级 CLAUDE.md两者叠加使用约束面会更完整。2.2 为什么普通提示词压不住 Sonnet 5.5很多人试过在对话里写一大段“你要小心”“不要乱改”“前后保持一致”然后发现没什么用。这不是提示词写得不诚恳而是规则密度不够、优先级不够、可验证性也不够。“小心”“保持风格一致”这种话模型没有对应的执行锚点。它能理解你的意图但没有具体的“上下文钩子”去触发该行为。真正有效的约束必须落到具体的文件路径、具体的命令、具体的顺序上。比如“不得修改src/util/目录下任何文件”就比“不要动工具函数”有效得多后者模型还要自己判断哪些算工具函数判断标准还跟你的不一致。优先级方面Claude Code 注入 CLAUDE.md 时会把它当作系统级指令初始权重高于普通用户消息。但如果后续对话里塞入大量代码块、报错日志、修改要求规则在长上下文中的权重也会被稀释。所以只靠一份 CLAUDE.md 还不够还得配合对话中主动引用规则编号把模型的注意力重新拽回规则文件这些我在后面第五节详细展开。2.3 和其他规则文件的横向对比很多人用过 AGENTS.md、.cursorrules或者干脆写一个prompt.md每次手动贴。它们的思路相近但落点和机制很不一样我整理了一个对比表方便你判断哪种更适合自己的场景。规则载体自动加载作用范围持续注入维护成本适用场景CLAUDE.md是当前项目目录及子目录每轮对话持续存在低随项目维护Claude Code 驱动的多文件重构、长会话任务AGENTS.md部分工具支持当前仓库视工具而定低多 agent 工具通用的团队约定.cursorrules是当前项目编辑器会话内中编辑器内的代码补全与单文件修改对话内临时规则否仅当次对话会被内容冲淡高一次性小任务、临时约束我的实际感受是CLAUDE.md 的优势是“持续存在”这四个字。像 Sonnet 5.5 做多文件重构这种任务动辄几十分钟、几百轮上下文只有持续存在的规则才有意义。对话内临时规则到了后半场基本等于没有AGENTS.md 虽然也被很多工具支持但在 Claude Code 里 CLAUDE.md 才是第一优先级的原生记忆入口。3. 我用的 CLAUDE.md 模板一份能管住重构的规则文件3.1 文件地图先告诉模型哪里能动、哪里别碰先说我踩过的坑最开始我写的 CLAUDE.md 全是“保持代码风格统一”“注意异常处理”这类话结果模型照样改飞。后来我把重心换成了文件地图——先建立项目分区明确每一层目录的职责、允许改动的边界、禁止触碰的红线。模型拿到这份地图后等于先知道了自己在哪个棋盘上落子。下面是一份我裁剪后的模板你可以直接改路径和模块名来用# 项目身份 当前项目代号模拟项目 X内部实验用 Web 服务。 你在这个项目中的角色是“重构执行者”不是架构师。 发现架构问题请记录到“发现但不处理”清单不要顺手重构。 # 文件地图 src/controller/ 路由层只做参数校验与响应组装。允许小幅改动。 src/service/ 业务层本次重构的重点目标。允许结构性改动。 src/repository/ 数据访问层除非任务明确要求否则禁止改动。 src/util/ 工具函数只读禁止改动。 src/config/ 配置模块改动时必须同步更新所有引用方。 # 改动权限边界 1. 禁止修改与任务无关的文件。判断标准改动内容是否与任务描述直接相关。 2. 禁止删除任何公共导出。 3. 禁止新增未使用的抽象类、接口、工具函数。 4. 如果发现既有代码有 bug记录到“发现问题”清单继续当前任务不要顺手修。文件地图的价值在于把“不要乱改”从一句空话变成了可执行的检查项。模型每准备动一个文件就会对照地图地图里写“只读”的目录除非任务里明确点名它不会碰。补上“禁止新增未使用抽象”这条后模型那种“顺手造一个新工厂函数”的冲动也被压住了大半。3.2 操作守则把“不要乱改”翻译成可执行步骤约束分两种一种是告诉模型“不准干什么”另一种是告诉模型“按什么顺序干”。前一种负责踩刹车后一种负责给路标。多文件重构最容易失控在过程中所以我在模板里写了很强的流程性规则重点不是结果而是每一步怎么走。# 多文件重构守则 1. 开始修改前先输出一份改造计划表 - 涉及的每个文件路径 - 每个文件的具体改动点 - 改动会影响哪些调用方 2. 计划表经确认后才能开始改文件。 3. 一次只改一个文件。改完该文件先自行检查语法、类型、上下文一致性再进入下一个。 4. 每完成 3 个文件必须停下来汇报进度等待确认。 5. 跨文件调用时先定位所有调用方再决定是否调整被调函数的签名。 6. 如果发现一个改动的牵连范围超出计划表立即停止并报告不要擅自扩大改动范围。 # 汇报格式每轮汇报必须包含 - 已完成文件数 / 计划总数 - 本次改动涉及的文件路径 - 新增或删除的公共接口 - 待确认的问题列表这几条规则的精髓是“原子化”和“过程断点”。一次只改一个文件模型犯错的代价就减少了十倍每 3 个文件停一次等于强制它在长任务里频繁重置注意力避免后半程的“抄近路”行为。尤其是第 6 条让模型在发现牵连范围扩大时“停下来问”而不是“猜着改”这一条帮我挡住过至少两次灾难性的重构。3.3 验收清单与冲突仲裁条款最后一部分是验收标准。没有明确验收命令的 CLAUDE.md就像没有体检流程的健身计划练成什么样全靠感觉。我习惯把验收写成一个模型无法“嘴上说完成”的命令序列让它必须实际执行才能得出结论。# 验收标准全部满足才算完成 1. 运行 npm run typecheck 必须通过。 2. 运行 npm run test 必须通过。 3. 运行 npm run lint 必须通过。 4. 新增代码必须维持原有注释风格和命名风格。 5. 若上述命令失败必须输出失败日志和修改建议继续修复不允许假装成功。 # 冲突仲裁条款 - 如果用户当前指令与 CLAUDE.md 规则冲突以用户当前指令为准。 - 但你必须明确指出冲突的具体规则条目并解释为什么当前指令需要覆盖规则。冲突仲裁条款是很多人会忽略的。模型在收到用户新指令时如果发现和 CLAUDE.md 冲突它会面临两难遵守文件还是听从当前用户如果不写清楚模型可能默默选一条执行也不会告诉你。有了仲裁条款它至少会把冲突暴露出来由我来判断。这本质上是一个“安全阀”防止规则文件本身变成束缚手脚的枷锁。4. 实测复盘让多文件重构按我的节奏走4.1 无规则状态下的翻车全过程拿我之前的一个项目举例代号叫模拟项目 X一个内部 Web 服务总共 14 个文件涉及改动。任务是把散落各处的全局配置读取方式改造成构造器传入的依赖注入方式。这个任务天然跨层controller、service、config 都有涉及也很考验模型对调用关系的把握。我第一次没加 CLAUDE.md直接让 Sonnet 5.5 做。它一次性改了 11 个文件diff 看起来工整但 review 时发现问题一堆两个纯工具函数被它顺手“优化”了三处调用方没同步修改一个公共接口被改名但调用方还挂着旧名字测试跑起来红了一片。我问它为什么改工具函数它的回答是“这些函数与配置读取有耦合属于合理清理”——这就是典型的“过度推理”它自己给任务加了不在范围内的工作。那次返工花了近两个小时其中大量时间浪费在“逐文件核对哪些改动是必要的”。最讽刺的是模型改出的代码本身水平不差差的是边界感和节奏感。它需要一个能管住它的流程而不是更强的代码能力。4.2 加入 CLAUDE.md 之后的执行过程我把第三节那份模板放进项目根目录重新执行了一个类似规模的重构任务。流程立刻变样了。模型第一轮没有再直接甩 diff而是输出了一张改造计划表每个文件改动点、影响范围、风险项。我看了一遍只调整了两个文件的顺序然后让它开工。改的过程严格执行“一次只改一个文件”和“每 3 个文件汇报一次”。汇报内容也确实按模板里的格式来了已完成数、涉及路径、公共接口变动、待确认问题。中间有一次它准备动 repository 目录下的文件理由是“配置获取放在 repository 更合适”我立刻回了一句“按 CLAUDE.md 文件地图repository 是禁止改动的”它马上调整了方案把改动控制在 service 层内。最后验收阶段它没有说“改完了”而是先跑了npm run typecheck和npm run test把两个失败项修正后才给出完成报告。整个过程的体验和之前完全不同我不再需要逐文件盯 diff只需要在关键断点上做判断。多花的时间基本只在开头读计划表的十来分钟但省内了大量纠偏和返工时间。4.3 前后对照三组数据说明问题我给自己做了个粗略记录把无规则和带规则两次重构做了对比。指标不多但很能说明问题指标无规则带规则无关文件改动次数3 次0 次公共接口被破坏次数2 次0 次中途人工纠偏次数4 次1 次最终验证通过耗时约 2 小时返工约 20 分钟总花费时长约 2.5 小时约 1.2 小时总时长只是参考毕竟两次任务规模不完全一样。但带规则之后最大的变化是“可预测性”我知道它下一步会做什么也知道它在什么节点需要我拍板。这种确定性在多文件重构里比什么都值钱。更何况我那份 CLAUDE.md 是一次性投入之后项目里所有重构任务都能复用成本早就摊薄了。5. CLAUDE.md 会失效的时候以及我补救的三层策略5.1 规则不是命令模型有权无视你必须承认一个现实CLAUDE.md 不是硬性约束它只是一段被反复注入上下文的文本模型仍然可能不遵守。最典型的是用户在对话里直接说“忽略 CLAUDE.md 里的某条规则”此时模型通常会顺从用户当前指令把规则暂时挂起。这不是 bug而是模型本身把“用户指令优先级”排在“项目规则”之上的结果。另一个失效场景是上下文稀释。任务进行到很后期上下文里堆满了代码片段、报错日志和中间结论CLAUDE.md 里的规则虽然还在但注意力权重会被大量新内容压过。这时模型不是故意违规而是“想不起”有这条规则了。表现为之前严格不碰的src/util/到后面开始动了之前按规定汇报到后期改成“我直接改完了你看下”。还有一种失效来自规则之间的自相矛盾。当 CLAUDE.md 里同时存在“保持原样”和“重构尽量彻底”两条规则时模型会选择它认为更符合当前任务上下文的那条而这个选择未必是你想要的。5.2 编号引用与主动锚定把规则重新拉回注意力对付上下文稀释我有个简单但很有效的办法给 CLAUDE.md 里的规则编号然后在后续指令里主动引用。比如中途发现模型开始动工具函数了我不会说“你怎么又乱改了”而是说“请重新阅读并执行 CLAUDE.md 第 3.1 节规则二检查你刚才的改动是否违反”。这相当于给模型一个明确的检索锚点强迫它去翻上下文里那部分规则文本比泛泛地批评有效得多。我还会在任务指令末尾固定加一句“开始前请先复述与本次任务相关的 CLAUDE.md 规则。”别小看这句话它让模型至少在任务开头把规则“念一遍”相当于把它从长期记忆里拉出来重新激活。虽然是同一个上下文但主动触发检索和不主动触发结果的稳定性差很多。实测下来加了这句话后模型在任务中期的规则遵从度明显提升。5.3 分层规则与自动验证让约束更抗稀释如果项目足够大一份 CLAUDE.md 写太满反而容易稀释核心规则。所以我会拆成两层根目录的 CLAUDE.md 只放全局原则和文件地图某个大模块子目录里再放一份 CLAUDE.md只约束该模块的特有改动规则。Claude Code 支持子目录级的规则读取这个能力很适合按模块隔离约束。比如在src/legacy-module/下放一份更严格的规则专门写“此目录不允许删除任何函数只能新增兼容层”。根目录规则不用管这些细节模型读子目录规则时也能更快找到针对性约束而不是从一大篇通用规则里翻找。最后一条防线是把验收命令写进规则让模型自己执行。带 CLAUDE.md 之后我几乎没有再遇到过模型“嘴上说完成但测试是红的”的情况因为它被要求必须实际跑命令、看输出。如果连命令都不跑那就说明规则已经被严重稀释此时我一般会让它“停止当前工作重新读取 CLAUDE.md 并列出本次任务相关规则”以此强制重置上下文锚点。这一步之后模型基本能回到正轨。说到底CLAUDE.md 不是写一次就一劳永逸的魔法文件。我自己的习惯是每次重构完回头把模型踩过的坑补成新规则过两轮任务后再删掉已经不重要的条款保持文件精简、编号清晰。它本质上应该像代码一样被维护而不是写完之后躺在仓库角落里吃灰。毕竟提示词工程这件事从来不是“写一次用永久”而是“和模型磨合的次数越多规则就越懂你”。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑