重构重型代码库
10 件套,给要拆 god class、切分单体、迁移到强类型的工程师。先盯死测试覆盖率,再批量 codemod,再丢给 AI agent,最后人工 diff 复核 — 上线不炸。
这个 pack 解决什么问题
你接手了一个文件。4,300 行,叫 OrderService.ts,团队里没人敢碰。你要拆它、给它加类型、或者干脆杀掉它 — 而现有测试覆盖率是 18%。让 LLM 听个 prompt 整个重写,是凌晨 2 点 prod 挂掉的路径。让自己手撸,是一个季度消失的路径。
这个 pack 是工程师真正用的流水线替代方案:先建覆盖率门禁,机械改动用结构化 codemod 解决 80%,剩下模棱两可的外科手术交给 AI agent,合并前用结构化 diff review 拦一道。每个工具对应一个特定阶段。按顺序装,按顺序用。
五阶段流水线
阶段 1 — 锁死行为(覆盖率门禁)
- Tidy First(Kent Beck 的纪律,打包成 skill)— 把结构性改动(重命名、抽取、移动)和行为性改动(逻辑)严格分开。纪律核心:永远不在同一个 commit 里同时做这两件事。这个 skill 强制执行。动手之前先看它。
- Technical Debt Manager(Claude Code agent)— 扫目标文件/模块,输出一份排序清单:先修哪个、爆炸半径多大、覆盖率缺口在哪儿会藏回归。用这份输出,在你动任何东西之前,给当前行为写 characterization tests。如果被动到的路径覆盖率 < 80%,停下来先补测试。这就是门禁。
阶段 2 — 机械改动(codemod,不是 LLM)
- ast-grep — 基于 tree-sitter 的结构化搜索替换。一条命令把 2,000 个文件里所有
getUserById(string)调用点改成getUserById({ id: string }),零字符串匹配误伤。80% 的机械重构活儿就该用这个。 - GritQL — 声明式 pattern 改写,比 ast-grep 抽象一点。涉及代码在块之间搬运(抽取方法、内联变量)时更好用。规则读起来像「如果 X 出现在 Y 附近,重写成 Z」就选 GritQL。
- Codemod — AI 驱动的迁移 CLI。用于开源社区已经写好的迁移(React class→hooks、Mocha→Vitest、Node http→fetch)。先查 registry 再考虑自己写 codemod。
阶段 3 — 模棱两可的外科手术(AI agent)
- Refactoring Specialist(Claude Code agent)— 安装路径
development-tools/refactoring-specialist。把那个有失败测试的文件交给它,清楚地告诉它:「把这三个方法抽出去成PricingPolicy类,OrderService的对外接口不变,不要动行为。」agent 给你一个 diff,你 review。 - code-simplifier — Anthropic 官方清理 subagent。agent 做完重活之后,对 diff 跑 code-simplifier 收掉那些无谓的啰嗦(嵌套三目、重复 guard)。外科级,限定范围。
阶段 4 — 收尸
- Unused Code Cleaner(Claude Code agent)— 拆完之后,diff 里会到处是死掉的 import、没人用的 export、孤儿 helper。这个 agent 用交叉引用找它们,不是 regex。最后一步跑 — 永远不要中途跑,因为重构进行中那些「没用」的东西常常马上要被重新接上。
- Legacy Modernizer(Claude Code agent)— 专门对应迁强类型这一支:无类型 JS → TS、Python → 类型化 Python、callback → async。如果重构包含语言/范式升级,阶段 2 codemod 跑完后用。
阶段 5 — 合并门禁(结构化 diff review)
- code-review-graph MCP — 给你的 code review agent(或你本人)提供图感知的 diff 视图:哪些 caller 受影响、对外接口改了什么、测试覆盖缺口现在落在哪儿。这是合并前最后一道门。如果图显示 diff 里有未覆盖的路径,打回阶段 1。
它们怎么协同
[阶段 1] Tidy First ──> Technical Debt Manager ──> characterization tests
│
▼(覆盖率 ≥80%)
[阶段 2] ast-grep / GritQL / Codemod ◀─── 80% 机械改动
│
▼
[阶段 3] Refactoring Specialist ──> code-simplifier ◀─ 模糊外科手术
│
▼
[阶段 4] Unused Code Cleaner ──> Legacy Modernizer(如有强类型迁移)
│
▼
[阶段 5] code-review-graph MCP ─────────────────> 合并
关键铁律:别跳过阶段 1。所有「AI agent 把代码库重构成废墟」的恐怖故事,开头都是缺一个 characterization test。agent 搬走了看起来死掉的代码,结果那段在生产里是承重的。测试能抓住这种事,别的都抓不住。
你会遇到的取舍
- ast-grep vs GritQL — ast-grep 更快、更简单、随处可装。GritQL 在 pattern 需要控制流时更强。默认 ast-grep;规则要表达「赋值之后、return 之前」时换 GritQL。
- Refactoring Specialist vs 自己写 diff — 200 行以下的拆分,用 agent 是杀鸡用牛刀。1,000 行以上的拆分,手撸是慢性自杀。甜点区:200-1,500 行外科级抽取,你心里已经有目标结构。
- Unused Code Cleaner 的时机 — 阶段 2 跑它,它会删掉阶段 3 马上要接上的代码。最后跑。永远最后。
- Codemod registry 覆盖范围 — 流行迁移(React/Mocha/Node)registry 是金矿。你公司内部 API 改名你得自己写 ast-grep 规则。别跟它较劲。
常见踩坑
- 覆盖率当虚荣指标 — 80% 指被动到的文件,不是整个 repo。agent 只关心爆炸半径。repo 全局覆盖率是给经理看的数字。
- 让 LLM 整个重写文件 — 把文件粘进 Claude,拿到「更干净」的代码,提交。这是凌晨 2 点收 page 的路径。上面这条流水线就是替代方案:小、可 review、有门禁的 diff。
- 阶段之间忘记 commit — 每个阶段产出一个可 review 的 diff。每个阶段边界都要 commit。后面 bisect 全靠这个。
- 重构没做完就跑 Unused Code Cleaner — 它会把阶段 3 马上要调用的 helper 删掉。每次都最后一步。
10 个资产打包就绪
常见问题
这套流水线适合多大规模的重构?
甜点区是 5-50 个文件、500-5,000 行变更。再小,手动改 + 一个 AI agent 就够。再大,得把重构拆成多次过这条流水线 — 别想着一次性合一个 5 万行的 diff,没有任何 review 流程在这种规模上能抓住问题。流水线靠重复 scale,不靠放大单次规模。
为啥用 ast-grep 而不是直接让 Claude 重写文件?
因为 ast-grep 是确定性的、幂等的、除非你明说否则不会引入任何行为变化。LLM 重写文件可能悄悄把 === 改成 ==、丢掉一个 try/catch、或者只改了 import 没改 usage 的变量名。ast-grep 做不到 — 它只做 pattern 说的事。LLM 留给那 20% 需要判断的活,机械的 80% 别让它碰。
我代码库覆盖率几乎为零怎么办?
那阶段 1 就是第一个 sprint 的全部工作。针对当前行为(什么样就什么样,含 bug)写 characterization tests。爆炸半径里有了测试,重构本身就是轻松活儿。跳过这一步是重构灾难最常见的单一原因。
10 个工具必须全装吗?能不能挑 3 个?
大部分重构只动 6 个:Tidy First(心智模型)、Technical Debt Manager(划范围)、ast-grep(机械改动)、Refactoring Specialist(外科手术)、Unused Code Cleaner(清场)、code-review-graph MCP(门禁)。另外 4 个是特定场景:GritQL 用于控制流改写、Codemod 用于已知迁移、code-simplifier 用于啰嗦清理、Legacy Modernizer 用于强类型迁移。挑阶段必装的那几个,剩下的按需。
动了 80 个文件的重构 diff 怎么 review?
别一个文件一个文件看 — 按 transformation 分组:「这 40 个文件是 getUserById 改名 findUser、这 25 个是抽出 PricingPolicy、这 15 个是死代码清理」。code-review-graph MCP 帮你生成这种分组。如果你在 PR 里要滚 80 个文件,说明那个 diff 没过流水线 — 打回去。