一个在老旧 PHP 项目里挣扎了两周的开发者,如何借助 AI 编程工具组合,把重构时间从预估的40小时压缩到11小时,并且代码质量反而更高。本文不是工具评测,而是一份真实战场上的操作手册。

起因:一块不敢碰的"屎山"
我手头有一个运行了5年的企业支付网关,核心模块是用 PHP 5.6 写的,大约1.2万行代码,没有单元测试,没有类型声明,函数之间通过全局变量传递状态。业务逻辑里还混杂着各种银行接口的硬编码参数。
上个月,我们需要对接一个新的支付通道,并且支持异步通知的幂等处理。在原代码上打补丁已经不可能------再打下去,我自己都看不懂了。
我决定重构支付请求与回调处理这一整块,但预估工期让我绝望:光是理解原有逻辑就需要两三天,再加上安全相关的敏感操作,手写+测试至少40个小时。
这一次,我决定让 AI 工具深度参与。我用的组合是:
- Cursor (Pro版,配置了Claude Sonnet 4 模型):作为主力 IDE,负责代码生成、补全、多文件编辑。
- Claude Code CLI:用于复杂的跨文件分析、重构方案设计,以及生成单元测试。
- 自定义的
.cursorrules和CLAUDE.md:定义项目规范和约束,让两个工具在同一个频道里工作。
下文是重构全程中沉淀下来的5个关键技巧。
技巧一:让 AI 先"读懂"代码,而不是直接改
很多开发者用 AI 重构时,直接把旧代码丢进去说"帮我重构成 TypeScript",然后收到一堆无法运行的幻觉代码。
我的做法是:先让 AI 输出一份"代码理解文档",由我确认后再动手。
操作步骤
- 在项目根目录创建
CLAUDE.md,写上基本要求:
markdown
# CLAUDE.md
## 项目背景
这是一个支付网关的旧版 PHP 代码,需要重构成 Node.js/TypeScript。
涉及敏感操作,任何涉及金额计算、签名验证、数据库事务的逻辑,必须保持原样。
## 输出要求
- 分析时给出原始代码依据,不要臆断
- 修改建议需要标明风险等级
- 不要删除任何原有注释,除非明确要求
-
在 Cursor 中打开整个项目文件夹,让 Cursor 自动索引(这一步利用它强大的项目级上下文)。
-
打开终端,运行 Claude Code,并指定要分析的目录:
bash
claude "分析 payment/ 目录下所有 PHP 文件,输出以下内容:
1. 每个文件的职责和主要函数列表
2. 函数之间的调用关系图(用文本描述)
3. 全局变量和常量的定义及使用处
4. 标记哪些部分有安全隐患或逻辑疑点
输出为 Markdown 格式,保存为 /docs/legacy-analysis.md"
-
Claude Code 运行了约3分钟,生成了一份2000多字的分析报告。它精确指出了:
processPayment()函数依赖的全局$config数组,在多个地方被引用- 一个回调签名验证逻辑里,
openssl_verify()的返回值判断有误 - 订单状态变更时缺少数据库事务保护
-
我花了20分钟审核这份报告,修正了一处误解,然后这份报告就成了后续所有重构操作的"圣经"。
为什么这步很重要?
如果让 AI 直接改代码,它在上下文窗口里看到的只是一个函数,无法理解整个模块的隐式约定。通过先输出分析报告,相当于让 AI 把它的理解"可视化",由人来把关,后续生成的代码才会准确。
技巧二:用 .cursorrules 把项目规范"注入"到每一次 AI 交互
Cursor 支持项目级别的 .cursorrules 文件,它会自动附加到每个 AI 请求的上下文中。这是保证 AI 输出符合团队规范的核心手段。
我的 .cursorrules 是这样的:
yaml
# .cursorrules
## 语言与框架
- 所有新代码用 TypeScript + Express.js
- 严格模式,禁止使用 any
- 支付相关逻辑必须使用 Result 模式返回,禁止抛异常
## 代码风格
- 函数不超过50行
- 每个函数必须有 JSDoc
- 所有外部 API 调用必须封装在独立的 service 层
- 金额使用 big.js 处理,禁止直接浮点数运算
## 安全约束
- 签名验证逻辑必须逐行保留,只改语法,不改逻辑
- 数据库操作必须使用参数化查询
- 任何涉及事务的操作必须标明"需人工审查"
## 当前重构阶段:第一阶段------拆解 payment-process.php
请只关注文件拆分和类型定义,不要改变任何业务逻辑。
这个文件让我能分阶段控制 AI 的行为。比如第一阶段只做拆分和类型化,第二阶段再优化调用关系。否则 AI 容易"顺便"把代码风格也改了,导致 diff 巨大,难以审查。
实际效果:
- Cursor 的 Tab 补全会自动遵循 big.js 的用法,不会出现原生浮点数
- 当我用
Cmd+K生成一个函数时,自动带上了 Result 类型的返回值 - 要求 AI 修改某个文件时,它不会顺手重构无关的代码
技巧三:用 Claude Code 的"多文件编辑"能力一次性完成拆分
当我把一个1800行的 payment-process.php 拆分成多个模块时,传统做法是:
- 手动切文件
- 一个一个复制函数
- 修改变量引用
- 解决循环依赖
这个过程极其枯燥且容易出错。我选择把这件事交给 Claude Code。
我的做法
- 先在 Cursor 中打开
payment-process.php,让 Cursor 索引该文件及其引用。 - 在终端进入项目目录,运行:
bash
claude "请将 payment-process.php 拆分为以下文件结构,遵循单一职责原则:
- services/payment/PaymentValidator.ts (验证支付参数、签名)
- services/payment/PaymentProcessor.ts (执行支付,调用银行接口)
- services/payment/CallbackHandler.ts (处理异步回调,幂等逻辑)
- services/payment/PaymentRepository.ts (数据库操作)
- types/payment.ts (所有接口和类型定义)
要求:
1. 严格遵循项目根目录的 CLAUDE.md 规则
2. 保持原有业务逻辑100%不变,只做拆分和 TypeScript 改写
3. 所有 import 路径使用相对路径
4. 生成后自动检查是否有循环依赖
5. 为每个新文件生成基本的 Jest 测试用例"
Claude Code 执行了约8分钟,连续操作了以下步骤:
- 读取原文件
- 分析依赖
- 生成6个新文件(含 types)
- 自动替换原文件中的 `require` 为 `import`
- 检测到一个循环依赖并自行修复
- 为每个 service 生成了测试文件,mock 了外部依赖
3. 我在 Cursor 的 Git 面板里逐文件审查了 Diff,确认逻辑没有被改动,然后提交。
这8分钟完成了我手工需要至少4个小时的机械工作,而且没有引入因疲劳导致的错误。
---
## 技巧四:AI 写测试,人审查覆盖缺口
重构支付模块,测试是安全网。但手写单元测试是体力活,尤其是为原有的各种边界条件写 case。
我的策略是:**让 AI 生成所有能自动生成的测试,然后我专注审查它遗漏的场景。**
### 具体步骤
1. 在 Cursor 中打开刚拆分的 `PaymentValidator.ts`。
2. 使用 `Cmd+K`,输入:
```text
为这个文件生成完整的单元测试,覆盖:
- 正常场景
- 参数缺失
- 签名错误
- 金额格式异常
- 订单号为空
使用 Jest,mock 掉外部依赖 crypto
Cursor 生成了一个约120行的测试文件,包含11个 test case。
-
我逐一审查,发现 AI 遗漏了:
- 签名验证时,参数排序不同但值相同的情况
- 金额包含千位分隔符
- 回调通知中
sign_type字段不存在
-
我手动补充了这3个 case,然后问 Cursor 另一个问题:
text
请检查我补充的3个测试用例的逻辑是否正确,并优化描述文字。
它修正了我手写的一个断言错误------openssl_verify 的返回值是 1 而非 true,这是我凭记忆写错的。
这一步的核心理念 :AI 和我形成了 "AI 生成骨架 + 人补边界 + AI 复查" 的协作闭环。最终这个文件有14个测试用例,覆盖了所有已知的支付验证场景。

技巧五:用 Git 分支策略控制 AI 的风险
AI 重构的风险在于,它可能在一个看似无害的改动里引入微妙的问题,而你在审查大量代码时容易忽略。
我建立了一套简单的分支策略来隔离风险:
bash
# 主干分支,保持稳定
main
# 每个 AI 任务一个独立分支,命名体现任务内容
refactor/payment-split # 拆分文件
refactor/payment-tests # 补充测试
refactor/payment-optimize # 优化性能
# 每晚合入一个经过人工审查的分支
工作流:
- 从
main拉出任务分支 - 在分支上让 Claude Code 或 Cursor 完成一项明确的任务
git diff查看所有改动,重点关注:- 金额计算逻辑是否被修改
- 数据库查询是否正确参数化
- 签名验证的算法是否完整
- 跑测试套件,并手动执行几个关键路径的端到端测试
- 合入
main
在整个重构过程中,我从未让 AI 直接操作 main 分支。每次合入前,我会问自己一句:"如果这个改动出了问题,最坏会发生什么?" 如果答案涉及资金损失,就再检查一遍。
重构结果与复盘
最终,支付模块的重构用时11个小时(包含中间的休息和等待),远低于预估的40小时。
| 指标 | 重构前 | 重构后 |
|---|---|---|
| 代码行数 | 12,400 (PHP) | 8,200 (TypeScript) |
| 文件数 | 18个混杂文件 | 6个职责清晰的文件 |
| 单元测试覆盖 | 0% | 87% |
| 静态类型覆盖 | 0% | 100% |
| 新增支付通道所需时间 | ~3天 | ~4小时 |
更重要的是,新代码的可维护性完全不是一个量级。下一周另一个同事需要对接另一个通道,他花了半天时间就完成了。
五个技巧总结
- 让 AI 先分析再动手:输出代码理解文档,由人确认后作为后续所有操作的基础。
- 用规则文件注入上下文 :
.cursorrules和CLAUDE.md是让 AI 始终遵守团队规范的廉价手段。 - 多文件操作交给 Claude Code:拆分文件、修复引用、检测循环依赖,这种机械工作 AI 做得比人好。
- AI 写测试,人查遗漏:形成协作闭环,把人的精力用在发现边界条件上。
- 用分支隔离风险:每个 AI 任务一个分支,人工审查后再合入,永远不让 AI 直接触碰主干。
这些技巧不仅适用于 PHP 到 TypeScript 的重构,任何遗留系统的现代化改造都能用上。关键是:把 AI 当作一个超级勤奋但缺乏业务常识的初级程序员,你来负责方向,它来负责执行。
你在用 AI 辅助重构的时候,踩过什么坑?或者有什么独门技巧?欢迎评论区交流。