代码重构这件事,我是怎么决定动还是不动的

上周组里有个争论,大概持续了两天。

起因是有人要重构一个三年前写的用户权限模块。代码确实难看——全局变量、硬编码角色字符串、权限判断逻辑散在各个地方。新来的同学看到之后直接说”这个得重写”。

然后我说了句:”等一下,先聊聊为什么要动它。”

场面沉默了几秒。


我在这个项目上工作了两年半。这个权限模块我见过它被骂,见过它被维护,也见过它在某次迁移中被绕着走。但它稳定运行三年没出过事故,每个季度大概有一到两次小改动。

“它很丑,但它不痛。”我说。

这话说出来之后我自己也想了想,觉得这句话可以当成我做重构决策的第一条原则。


先问”不动的代价”,再问”动的代价”

大多数人讨论重构的方式是:这代码写得这么烂,我们要不要重构?

但这个问题的答案几乎永远是”要”,因为代码几乎永远可以写得更好。这是个伪命题。

我现在问的问题是:如果不动它,接下来三个月我们会付出什么代价?

代价是具体的东西,不是”技术债会越来越高”这种抽象说法。是:

  • 下次加功能,开发要多花几天?
  • Bug排查的时候,定位成本是多少?
  • 有多少人真正会动这块代码?

如果回答完这些问题之后,”不动的代价”比较低——比如上面说的权限模块,每季度改一次,每次改动也就一两天——那不动是理性选择。

如果”不动的代价”很高——比如某个核心链路每周都要改,每次改都要小心翼翼地测一遍——那才是真正该动的时机。


有一次我动错了

2024年初,我重构了一个消息队列的消费者模块。

理由充分:代码耦合重,回调嵌套三层,单元测试覆盖率接近零。我用了两周,把它改成了事件驱动架构,测试覆盖率拉到了70%。

然后业务方告诉我们,这个功能三个月后要下线。

两周工作量,换来三个月的维护期。然后代码死了。

我不是说重构本身是错的。是我在动它之前没有问:”这个模块的生命周期还有多长?”

这也是我现在的第二个问题:这块代码接下来还会存活多久?

还有六个月的模块,不值得花两周重构。还要活三年的核心模块,花一个月梳理清楚是值的。


增量优于大爆炸

我见过太多”完整重构”计划——把老代码冻结,新代码从头写,双跑一段时间,然后切换。

理论上很美,实践里很难。因为:

老代码里有很多隐性知识。它为什么绕那个弯,为什么有那个特判,为什么那个字段默认值是0不是null——这些都是靠 git blame 和 issue 能找到的历史,但很少有人在重构前把这些全读完。

新代码写完上线之后,总会遇到老代码处理过、新代码遗漏的边界情况。

我现在更偏向的做法是:划出最小可替换单元,逐块替换,而不是整体重写。

一个大函数,先把它拆成几个小函数,逻辑不变。
一个上帝类,先把其中一个职责提取出来,其他的先放着。
一个全局状态,先在新路径上用局部状态,老路径先不动。

这样每一步都可以独立测试,每一步都可以随时停下来。


AI帮我做了什么

去年我开始用AI辅助做一些重构的前期工作。

主要是两件事。

一是让它读代码,帮我总结”这个模块到底在干什么”。写文档这件事,AI比我有耐心,而且它能发现我因为太熟悉代码而忽略的东西。有一次它在总结里写道:”这个函数在A路径和B路径下对同一个字段做了不同假设”——这是个潜在Bug,在代码里藏了很久,我一眼看不出来。

二是让它帮我评估重构影响范围。给它代码,问它”如果我改这个接口的签名,有哪些地方会受影响”。它不一定100%准确,但能帮我把搜索范围快速缩小。

但我不让AI直接帮我做重构决策。

因为重构决策里有很多上下文是AI不知道的:这个模块下季度要大改、这个团队目前只有一个人熟悉这块、这个项目六个月后要下线……这些信息不在代码里,AI给不了。


一个实用的三问法

聊到最后,我整理了一下自己做重构决策的思路,大概是三个问题:

不动它,接下来会付出什么代价?
要具体,不要抽象。下次改动多花几天,每月要修几个Bug,定位一个问题要花多少时间。

这块代码还要活多久?
对生命周期短的模块,审美上的烂可以接受。对长期活着的核心路径,烂会变成慢性毒药。

能不能增量改,而不是整体换?
几乎总能找到比”推翻重来”更小的切入点。越小的步子,越容易踩稳。

回到最开始那个权限模块。

讨论完之后,我们没有重构它。但我们在它外面加了一个新的权限检查适配层——下次有新需求,走新层,老的暂时不动。

不是因为老代码好,是因为这是目前代价最小的走法。

有时候聪明的工程决策,就是选那个最无聊的选项。


上一篇:代码里最难的不是写逻辑,是起名字


代码重构这件事,我是怎么决定动还是不动的
https://www.ohtudou.top/2026/04/27/2026-04-27-code-refactor-decision-making/
作者
Tudo
发布于
2026年4月27日
许可协议