通义灵码在 IDE 里补全单行代码很顺,跨文件重构却是另一类任务:影响面大、依赖多、出错后难定位。AI 在这里能帮你把改动铺开,但边界、顺序和验收仍然要人来定。下面按先划范围、再出计划、小步改、分层验四步讲,附四类重构的验收标准。
先划三个清单
动手前把三件事写下来:要改的文件与目录、不能碰的模块、必须保持不变的对外接口。
第二项最容易被跳过,也最要命。计费、权限、审计日志、灰度开关,这些模块往往和业务代码缠在一起,模型看到风格不一致就会顺手一起改。它不知道那段看起来别扭的代码,是为了绕开一个历史问题。
第三项决定改动能不能上线。HTTP 路径、字段名、错误码、消息格式、数据库字段类型,只要有一项变了,调用方就得跟着改。把这些列成不可变清单,写进提问里。
判断标准:如果"不能碰"这一栏写不出具体模块名,说明还没准备好,先花半小时读一遍调用关系。
先要计划,不要直接要改动
让它一次性给出全部改动,看起来高效,实际把风险都藏进了 diff。更稳的做法是先要一份计划:按文件列出改哪些地方、依赖关系是什么、建议的执行顺序、每一步怎么验证、风险在哪。
计划里必须能看到哪些调用方会受影响。看不到,说明它没读到完整的项目结构,或者索引范围不全,这时候先确认它能看到哪些文件,再谈重构。
计划还要包含"需要人工确认"的条目。一份全是确定语句的计划,通常意味着它忽略了约束。把团队规范写进提问比事后挑错省力,把通用模板改成项目约束的方法在这里同样适用。什么时候需要它多想几步,也可以对照任务类型的判断方式。
小步走:一次一个关注点
重命名、提取函数、移动模块、替换实现,这四类改动分开做。混在一次提交里,出问题时无法判断是哪一步引入的。
每一步单独提交,提交信息写清"只做重命名,无逻辑变更""只提取函数,行为不变"。评审的人能按类型看 diff,回滚时也只退一步。每次重构的提问与结论建议留档,会话历史的管理方式可以直接借用。
改完立刻编译、跑单测,不要攒到最后一起跑。跨文件改动的问题常在第二十次修改时才暴露,越晚发现,定位成本越高。
三层验证,加一层对比
先编译与类型检查,过了说明语法和签名没问题。再跑已有单测,过了说明没破坏已覆盖的行为。最后关键路径手工走一遍,自动化覆盖不到的地方只能靠人。
再加一层:同一组请求,对比重构前后的响应。字段、类型、值、以及有语义的顺序,都要一致。这一层能抓住编译通过、测试通过但语义变了的情况,比如异常被吞掉、事务边界被挪动、默认值改了。
老项目没有测试怎么办?先补描述现有行为的测试:用几组真实输入跑一遍旧代码,把输出记下来当基线,再动重构。
重构前检查单
- 不可变清单:对外接口、错误码、字段类型
- 禁区清单:计费、权限、审计、灰度
- 基线:旧代码的输入输出样例,至少 5 组
- 退路:当前分支已提交,能一键回到改动前
- 验证:编译、单测、关键路径、响应对比
四类重构的边界与验收
| 重构类型 | 边界怎么划 | 验收标准 | 常见翻车点 |
|---|---|---|---|
| 全局重命名 | 限定目录与语言范围,排除生成代码、配置文件和字符串里的同名文本 | 全仓库搜索旧名称为零命中,编译与单测通过 | 反射调用、序列化字段名、日志检索规则被漏改 |
| 提取公共函数 | 先确认各调用点行为真的一致,差异用参数表达 | 每个调用点的输入输出与改造前逐组对照 | 把细微差异抹平,边界值行为变了 |
| 拆分模块或服务 | 先定接口契约与依赖方向,再动代码 | 契约测试通过,旧调用方无需同步发版 | 循环依赖被藏进新模块,事务跨了边界 |
| 替换第三方依赖 | 列出实际用到的接口与版本差异 | 关键路径压测数据不劣化,异常分支行为一致 | 默认超时、重试策略、错误类型不同 |
改动范围之外的盲区
它看不到运行时行为。哪个分支在生产上真的会走到、哪个异常被上层吞了、哪段代码靠反射调用,这些只能靠日志和调用链确认。
跨仓库、多语言、动态加载的项目,索引经常不全。它改不到的地方不会报错,只会留给你一个"看起来改完了"的假象。改动范围越大,越要自己搜一遍旧符号是否还有残留引用。
它无法判断语义是否等价。把循环改成流式、把同步改成异步、把锁的粒度调细,编译都能过,行为可能完全不同。涉及并发、事务、缓存的地方,diff 必须逐行看。
它也不能替你做代码评审。生成量大时,人容易扫一眼就点通过,这比不审更危险。
动手前后的三个疑问
改动涉及 30 多个文件,怎么分组?按依赖方向分:先改被依赖的底层,再改调用方;每组不超过 8 个文件,一组一提交;组与组之间跑一次全量编译。
改到一半发现影响面比预想大,怎么办?停在当前这一步,先把已改部分补到可编译、可提交的状态,再重新评估范围。不要在编译不过的半成品上继续扩大改动,那样连回退点都没有。
它改完之后,评审重点看什么?看四样:异常处理是否被改动、事务与锁的边界、默认值与空值处理、旧符号是否还有残留引用。这四样编译和测试都抓不到。


