1421 字
7 分钟
我用AI辅助重构了5000行遗留代码,踩了三个大坑

我用AI辅助重构了5000行遗留代码,踩了三个大坑#

上个月接手了一套老系统,大概5000多行代码,全是if-else嵌套,最深层有6级。维护这玩意儿的痛苦程度,大概相当于用勺子挖隧道。

正好那段时间Claude Code比较成熟了,我心想:能不能让AI帮我重构一下?

结果呢?活是干完了,但过程里踩的坑比我预想的多得多。写下来给想走这条路的人当参考。

坑一:AI看不懂业务上下文#

重构第一天,我信心满满地把代码丢给它,说了句”帮我拆成小函数”。

它确实拆了。5000行变成了30个函数,每个不超过50行,命名清晰,结构工整。表面上看漂亮极了。

但跑了单元测试,挂了8个。

原因很蠢:有一段代码里,status 字段在不同场景下有不同含义。正常流程里它是”订单状态”,但在异常处理分支里,它临时被复用为”重试次数”。这个逻辑没有任何注释,纯靠变量名的上下文来区分。

人类开发者的经验告诉我们:同一变量在同一作用域里不应该改变语义。但AI严格按照字面逻辑拆分,它看不出来这个字段被”偷换”了。

我的教训:给AI的输入不能只给代码,必须给业务上下文。

后来我改了策略:

原始代码片段:
// 这里status在正常流程是订单状态:pending/confirmed/shipped
// 但在retry分支里被临时复用为重试计数器
// 请保留这个逻辑,但拆分函数
重构要求:
1. 不要改变status的复用逻辑
2. 先处理异常分支,再处理正常分支
3. 拆成3-5个函数,每个负责一个明确的阶段

第二次效果好多了,挂的测试降到了2个。

坑二:AI偏好”完美”方案,不是”安全”方案#

这是第二个让我头疼的问题。

我的目标是渐进式重构——每次改一点,确保能跑、能回滚。但AI给我的方案是一次性大改。

它把整个文件拆成了四个模块,引入了一套全新的事件驱动架构。代码确实漂亮了,但改动量太大,万一出问题根本没法排查。

我跟它说:“别搞新架构,保持现有结构,只做函数级别的拆分。”

它又说了一遍事件驱动方案,换了个更”优雅”的写法。

第三次我才意识到:AI倾向于给它训练数据里评分最高的方案,而不是最适合你当前处境的方案。 训练数据里,“优雅架构”的文章比”保守重构”的文章点赞多,所以它优先选前者。

最终我是这样解决的:先让它只做最保守的改动(纯提取函数,不改变任何逻辑),改完一轮跑测试,通过了再进入下一轮。每一轮之间加一个commit,随时能回滚。

Terminal window
# 我的重构节奏
git commit -m "重构前基线"
# AI重构第一轮
git commit -m "提取异常处理函数"
git push
# 跑测试,通过
# AI重构第二轮
git commit -m "拆分主流程为阶段函数"

这个节奏看起来很笨,但它保住了我的周末——不用半夜爬起来修生产问题。

坑三:AI忽略了边界条件#

最后一批改动提交后,测试全绿了。我差点就要发PR。

但我决定手动过一遍代码。这一过,发现问题了。

有一个函数里,原来的代码写了:

// 最多重试3次,超过就放弃
if (retryCount > 3) return fallback;

AI重构后变成了:

const MAX_RETRIES = 3;
if (retryCount >= MAX_RETRIES) return fallback;

等等。> 3>= 3 不一样啊。原来是可以重试3次(第4次失败才放弃),现在变成只重试2次(第3次失败就放弃)了。

这看起来是个低级错误,但问题是:这种边界条件的细微变化,自动化测试不一定覆盖得到。 我们的测试用例里,恰好没有重试次数的边界用例。

类似的问题还有:

  • null检查被改成了undefined检查(业务逻辑里这两者处理方式不同)
  • 一个forEach被换成了map(后者会返回新数组,影响后续引用判断)
  • 时间格式化的时区信息丢失了(测试环境和服务端时区碰巧一致,没暴露出来)

结论#

AI辅助重构这件事,我是认可的。但这5000行改下来,我的体会是:

AI是极好的执行者,但你是产品经理。 它负责把方案落地,你负责决定方案对不对。具体到重构这件事:

  • 给它的上下文要比你以为的”够多了”再多一倍
  • 每一轮改动要小,小到出了问题一眼就能看出来
  • 不要相信测试全绿,肉眼过一遍代码仍然不可少
  • 最危险的不是AI写错了,而是AI写得很对但不是你要的

下次再做类似的事情,我还会用AI。但不会再用”帮我把这段代码重构一下”这种偷懒的方式。你得把它当一个聪明但缺乏业务经验的实习生来带。

如果你也在用AI做重构,有什么踩坑经验?欢迎交流。