我用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,随时能回滚。
# 我的重构节奏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做重构,有什么踩坑经验?欢迎交流。