代码重构安全清单:别把重构做成重写
重构最容易失控的地方,是你以为自己在整理代码。
结果改着改着变成:
1 | 顺手改业务 |
最后没人知道这次改动到底想解决什么。
重构不是重写。
真正好的重构,应该让外部行为基本不变,只让内部结构更清楚。
一、先说清楚为什么要重构
不要用一句:
1 | 代码太乱了。 |
作为重构理由。
这句话太大,最后会导致无限范围。
更好的目标是具体的:
1 | 把订单创建逻辑从 controller 挪到 service |
重构前先写一句目标。
如果一句话说不清楚,说明范围还没收住。
二、重构前先建立验证方式
没有验证方式的重构,本质上是靠感觉。
至少要有一种:
- 单元测试
- 集成测试
- API 回归测试
- 页面截图对比
- 核心流程手动验证清单
- 日志和监控观察项
比如重构登录流程前,先列清楚:
1 | 正确账号可以登录 |
有测试最好。
没有自动化测试,也要有手动验证清单。
不要等改完才想“我应该怎么知道没坏”。
三、先补测试,再动结构
如果你要重构的是核心逻辑,最好先补测试。
比如价格计算:
1 | 原价 |
这种逻辑很容易改坏。
先写几个典型用例:
1 | 普通用户无优惠 |
等测试能覆盖当前行为,再开始改内部结构。
重构时测试失败,才知道自己动到了行为。
这会让你心里稳很多。
四、一次只改一种东西
重构最怕混合改动。
比如一次提交里同时做:
1 | 改目录结构 |
代码 review 会非常痛苦。
更好的方式是拆开:
1 | 第 1 次:纯移动文件,不改逻辑 |
每一步都小一点。
小步提交的好处是:一旦出问题,容易定位,也容易回滚。
五、先做机械改动,再做设计改动
机械改动指的是:
- 重命名
- 移动文件
- 调整 import
- 格式化
- 抽取函数
这些最好先单独完成。
设计改动指的是:
- 改职责边界
- 改数据流
- 改状态机
- 改错误处理方式
- 改模块依赖关系
如果机械改动和设计改动混在一起,diff 会非常难看。
review 的人很难判断:
1 | 这是只是挪位置? |
所以先把机械部分做干净,再处理真正需要思考的部分。
六、抽函数不是越多越好
很多人重构时喜欢疯狂抽函数。
但函数太碎,也会降低可读性。
判断一个函数是否值得抽,可以看:
- 是否有明确业务含义
- 是否能减少重复
- 是否能降低当前函数复杂度
- 是否方便单独测试
- 名字是否比代码本身更清楚
不要为了“短”而抽函数。
比如:
1 | check() |
这种名字没有信息量。
抽出来也不一定更好。
好的函数名应该解释意图:
1 | validateOrderStock |
七、保持接口边界稳定
如果这次目标是内部重构,尽量不要同时改外部接口。
外部接口包括:
- HTTP API
- CLI 参数
- 配置文件字段
- 数据库表结构
- 消息格式
- 对外 SDK
内部代码你可以调整。
但外部契约一变,影响面立刻扩大。
如果必须改接口,最好单独做兼容:
1 | 先支持新旧字段 |
重构的理想状态是:
1 | 调用方不知道你重构过。 |
八、删除代码要比新增代码更谨慎
删除代码很爽。
但要确认它真的没人用。
可以检查:
- 全局搜索引用
- 路由注册
- 定时任务
- 消息消费者
- 配置开关
- 外部调用方
- 线上日志
有些代码看起来没人调用,但可能通过反射、配置、脚本、任务调度入口使用。
特别是后台任务、Webhook、CLI 子命令,不能只靠 IDE 引用判断。
删除前先确认入口。
九、重构后要看 diff
改完代码后,不要直接提交。
先自己看一遍 diff。
重点看:
- 有没有混入无关格式化
- 有没有意外改业务判断
- 有没有多余日志
- 有没有调试代码
- 有没有改到不该改的文件
- 有没有让错误信息变差
- 有没有把简单逻辑抽得更绕
很多问题在自查 diff 时就能发现。
这一步很朴素,但非常有效。
十、写清楚提交说明
重构提交不要只写:
1 | refactor |
更好的写法:
1 | 重构订单创建流程,拆分参数校验和库存检查 |
或者:
1 | 调整用户模块目录结构,保持接口行为不变 |
提交说明要让未来的人知道:
1 | 这次改动的意图是什么 |
如果有行为变化,就不要假装只是重构。
那应该叫功能修改或 bug fix。
十一、一个实用重构清单
每次重构前,可以快速过一遍:
1 | 1. 这次重构目标能否一句话说清楚? |
这个清单不复杂。
但它能挡住很多“越改越大”的重构。
十二、我的建议
好的重构应该像整理房间。
你知道要整理哪一块。
你不会顺手把墙拆了。
你整理完之后,东西更容易找,房间还能正常住。
写代码也一样。
重构的目标不是证明自己能写更高级的结构。
而是让下一次修改更简单、更安全、更不容易出错。