👁 47 次阅读

配图

先把目标说清楚:重构不是重写
遗留项目重构最常见的失败,不是模型不会写代码,而是团队把重构当成重写:一边改结构,一边改行为,一边补功能,最后没人能判断问题来自哪一步。更稳妥的定义是:在不改变外部可观察行为的前提下,降低理解成本、减少重复、收窄耦合、补齐测试护栏。Claude Code 适合做的是读代码、归纳模式、提出小范围修改、生成验证命令和整理提交说明,不适合在没有基线、没有验收标准时直接大改。开始前先写下一句话:本次只解决哪一个具体问题,哪些行为必须保持不变,哪些文件不允许碰。把这句话放进任务说明,也要放进仓库里的协作文件。
建立安全基线:先知道现在是什么样
在让 Claude Code 改任何文件之前,先冻结现状。创建独立分支,确认工作区干净,记录构建、测试、静态检查的当前结果。即使测试不全通过,也要把失败项记下来,否则后续无法区分旧问题和模型引入的新问题。建议至少执行以下命令:
git status
git checkout -b refactor/legacy-baseline
git log --oneline -n 10
rg --files | wc -l
npm test
npm run build
如果项目使用 Maven、Gradle、Go 或 Python,把测试与构建命令替换成项目真实命令。把结果写入 BASELINE.md:当前可运行命令、已知失败、构建耗时感受、高风险目录、外部依赖。这个文件不是形式主义,它是后面判断「有没有变坏」的参照物。
用 CLAUDE.md 给协作划边界
Claude Code 在仓库中工作时,会依赖上下文文件理解项目约定。与其每次在对话里重复规则,不如在根目录维护 CLAUDE.md,把项目地图、常用命令、禁区、验收标准写清楚。内容不必长,但必须可执行。示例:
# 项目约束
- 修改前先阅读 README、构建脚本与测试脚本
- 不改变公共 API,除非任务明确要求
- 一次只处理一个重构边界
- 先给出修改计划,再改代码
- 修改后必须运行测试、lint 与构建
- 发现无关问题只记录,不顺手修改
- 无法确认的行为写入 RISKS.md
再补充模块入口、数据层位置、外部服务适配层、生成代码目录和不要触碰的第三方目录。规则越具体,模型越不容易把「顺手优化」变成不可审查的大 diff。
第一步:让 Claude Code 建立项目地图
不要一上来就问「帮我重构这个项目」。先让它只读,输出项目地图。可以启动交互式会话,然后粘贴任务:
claude
请只读扫描当前仓库,不要修改文件。输出:
1. 顶层目录职责
2. 应用入口与启动命令
3. 测试命令与构建命令
4. 核心领域模块与外部依赖
5. 高风险目录与生成代码目录
6. 建议优先阅读的文件清单
每个结论尽量给出文件路径作为证据。
拿到结果后不要全信。抽查它引用的文件,确认路径存在、职责描述与代码一致。把确认后的内容整理进 MODULE_MAP.md,并标注「已人工核对」与「待确认」。这张地图会成为后续拆分任务、控制上下文和审查 diff 的基础。
定位坏味道:从症状追到证据链
重构任务要写成「症状 + 证据 + 验收」,而不是「感觉这里不好」。可以先用搜索命令找线索,再让 Claude Code 归纳。例如:
rg -n 'TODO|FIXME|HACK' src
rg -n 'deprecated' src
rg -n 'catch' src
git log --oneline -- src
搜索结果只是线索,不是结论。让 Claude Code 对每条线索回答:出现在哪个文件、被谁调用、是否有测试覆盖、改动会影响哪些入口。下面这张表可作为任务筛选的参考:
| 症状 | 可查证据 | 适合交给 Claude Code 的任务 | 产出 |
|---|---|---|---|
| 重复代码多 | 相似函数名、复制片段、重复分支 | 归纳重复模式并提出合并边界 | 重复清单与合并建议 |
| 测试脆弱 | 失败集中、断言依赖实现细节 | 读取失败日志并分类 | 失败原因与最小修复建议 |
| 模块耦合重 | 大文件、循环引用、跨层调用 | 解释依赖关系并画边界 | 拆分草案与非目标 |
| 外部依赖旧 | 调用点分散、适配层缺失 | 汇总调用面并建议隔离层 | 适配层改造清单 |
表格里的任务都应先「只分析不修改」。确认边界后,再进入代码修改。
把大重构拆成可审查的小任务
遗留项目最怕一口气改十个文件。把重构计划写成任务账本,每个任务只解决一个边界,并写清范围、非目标、涉及文件、验证命令和回滚方式。可以让 Claude Code 先产出计划,但要求它不要改代码:
请基于 MODULE_MAP.md、BASELINE.md 与 RISKS.md 生成重构计划。
每个任务必须包含:范围、非目标、涉及文件、验证命令、回滚方式。
按风险从低到高排序,先不要修改任何文件。
建议把任务拆成「提取纯函数」「收窄参数」「隔离外部调用」「补充特征测试」「替换重复实现」这类可验证动作。一个任务如果无法用一条测试命令或一次 diff 审查来判断成败,就说明它还太大。
先读后写:让模型解释代码而不是直接动手
在修改前,让 Claude Code 解释目标代码的输入输出、副作用、异常路径和调用方。提示词可以这样写:
阅读 src/legacy/order.ts,只输出分析,不修改文件:
1. 输入与输出
2. 副作用与外部依赖
3. 异常路径
4. 可测试边界
5. 不建议改动的部分
6. 需要补充的特征测试
如果它的解释引用了不存在的函数或文件,立即要求它给出路径并重新核对。解释阶段的目标不是得到漂亮文档,而是暴露理解偏差。只有当你和模型对「当前行为是什么」达成一致,才进入修改阶段。
小步修改:一次只改一个边界
每个任务单独开分支,要求 Claude Code 先给修改计划,再执行最小 diff。示例任务说明:
git checkout -b refactor/order-boundary
在不改变外部行为的前提下,把订单计算中的日期格式化抽成纯函数。
要求:
- 先列出将修改的文件与原因
- 保持公共 API 不变
- 补充边界条件测试
- 修改后运行项目测试命令
- 发现无关问题只记录到 RISKS.md,不顺手修改
执行后先看 diff 统计,再看具体差异。重点检查三类问题:是否改了公共接口,是否把行为变化混进重构,是否删除了看似无用但被动态调用的代码。若 diff 超出任务范围,停止并拆任务,不要用「既然改了」说服自己继续。
验证:用多层检查挡住模型幻觉
模型可能给出看似合理但实际不存在的 API、测试或配置。验证不能只靠对话,要落到命令和人工审查。建议按顺序执行:
- 自动测试:运行与改动模块相关的测试,再运行全量测试或冒烟测试。
- 静态检查:运行 lint、类型检查、格式检查,确认没有新增警告。
- 构建验证:执行真实构建命令,避免只在开发模式通过。
- 差异审查:查看 diff 统计与逐行差异,确认没有无关改动。
- 行为对照:与 BASELINE.md 对比,确认旧失败没有扩大,新失败有解释。
npm test
npm run lint
npm run build
git diff --check
git diff --stat
git diff
如果项目没有测试,不要直接大改。先让 Claude Code 基于现有行为生成特征测试,只记录现状,不修正疑似 bug。测试通过后,再开始重构。
测试缺失时:先写特征测试再动结构
遗留系统常见情况是测试缺失或只覆盖主路径。此时可以把「补充特征测试」当成独立任务,而不是重构的附带动作。让 Claude Code 先列出可测试的接缝:纯函数、适配层、序列化边界、命令入口、外部服务封装。对每个接缝生成最小测试,输入来自真实样例或手工构造,断言只描述当前行为。提示词示例:
为当前模块生成特征测试,目标是固定现状行为,不是修正 bug。
要求:
- 先列出可测试接缝
- 每个测试说明覆盖的输入与输出
- 不修改生产代码
- 对不确定的行为标注待确认
特征测试通过后,再让模型重构。这样即使结构变化,也能通过测试对照行为是否保持。
提交与回滚:让每次重构都可追溯
提交信息要说明「改了什么结构、保持了什么行为、如何验证、还有什么风险」。示例:
refactor(order): extract date formatting helper
- 保持公共 API 不变
- 抽取纯函数并补充特征测试
- 验证:npm test, npm run lint
- 风险:日期时区行为保持现状
提交前使用分块暂存,避免把调试代码和无关格式变化混进去:
git add -p
git diff --cached
git commit
git tag refactor-checkpoint
如果验证失败,优先回到任务分支修正,不要在主分支上试错。需要放弃时,用新分支或还原指定文件,避免影响其他人的工作区。
常见坑与应对
- 一次让模型改太多:把任务拆到单个边界,要求先计划后修改。
- 把解释当事实:要求给出文件路径、调用方和验证命令,再人工抽查。
- 测试通过就放心:补充 diff 审查与行为对照,防止测试没覆盖到。
- 上下文过期:模块地图和风险清单要随任务更新,不用的旧结论及时删除。
- 提交信息含糊:固定模板,写清范围、非目标、验证与风险。
- 顺手修无关问题:只记录到 RISKS.md,另开任务处理。
一套可复用的日常流程
- 开工前确认工作区干净,创建重构分支,记录基线测试与构建结果。
- 让 Claude Code 只读生成模块地图,人工核对后写入 MODULE_MAP.md。
- 用搜索命令找症状,让模型整理证据链,形成 REFACTOR_PLAN.md。
- 从低风险任务开始,每个任务单独分支,先解释代码再要求修改。
- 修改后运行测试、lint、构建与 diff 检查,和基线结果对照。
- 提交信息写清结构变化、行为保持、验证命令与剩余风险。
- 合并前让另一位同事按任务说明审查,不只看出代码是否漂亮。
最后给一段可执行建议:选一个调用面清楚、风险较低的遗留模块,先建立分支和基线,再让 Claude Code 只读输出模块地图与风险清单;然后只选一个可验证的小边界,要求它先给修改计划和测试点,确认后再改代码;最后用测试、静态检查、构建和 diff 审查收口。把这次过程写成模板,下一次重构直接复用。