接手一个运行了三年的老项目,代码质量参差不齐。有的模块写得漂亮得像教科书范例,有的模块却像从 Stack Overflow 上随机粘贴拼凑出来的。面对这样的代码库,重构是必然的选择,但怎么重构、从哪里开始、重构到什么程度,是比写新功能更难回答的问题。

维度一:理解业务,而不是理解代码

这是最重要也最容易被忽略的一步。在动任何一行代码之前,先搞清楚这段代码在业务中扮演什么角色。它承载的是什么业务流程?它的输入和输出分别是什么?它的边界条件在哪里?只有真正理解了业务意图,你才能在重构时做出正确的取舍,而不是机械地把旧代码翻译成新写法。

我的做法是为每个待重构的模块写一份"业务说明文档",用自然语言描述这个模块做了什么、为什么这么做、有哪些已知的历史问题和边界情况。这个过程本身就是一次深度的代码审查,经常能发现逻辑上的不合理之处。

维度二:建立安全网

重构的前提是拥有可靠的测试覆盖。如果当前代码没有测试,那第一步不是重构,而是补测试。这些测试不需要是完美的单元测试——在遗留代码上追求 100% 的单元测试覆盖率既不现实也不经济。更务实的做法是:先补集成测试和端到端测试,覆盖核心业务路径,然后再逐步下沉到单元测试。

维度三:识别接缝

遗留代码最大的问题往往是耦合。一个函数调用连到另一个函数,再连到第三个函数,牵一发而动全身。重构的第一步不是拆,而是找接缝——哪些模块之间的依赖是合理的,哪些是不合理的;哪些接口设计是清晰的,哪些是混乱的。找到接缝之后,你才能制定出最小侵入性的重构方案。

维度四:渐进式改造

不要试图一次性重写整个模块。渐进式重构的风险更可控,每一步的变更范围都足够小,出问题时可以快速定位和回滚。具体策略是:先拆分,再优化。先把一个大的、职责混乱的模块拆成几个小的、职责清晰的模块,保持外部接口不变;然后再逐一优化每个小模块的内部实现。

维度五:保持兼容

重构期间,新代码和旧代码会共存一段时间。这意味着你需要维护一定的向后兼容性。我常用的策略是"适配器模式"——创建一个新的、干净的接口,背后通过适配器连接到旧的实现。等所有调用方都迁移到新接口后,再逐步移除旧的实现。

维度六:文档与共识

重构不是一个人的战斗。你的重构决策需要被团队理解和认可。重要的架构变更应该有对应的设计文档,记录为什么选择这种方式而不是另一种,有哪些已知的权衡。好的文档不是代码的说明书,而是决策的记录——让后来者理解"当时为什么这么做"。

重构是一场持久战,不可能毕其功于一役。关键不在于一次做多少,而在于每一次都让代码比之前好一点点。正如 Kent Beck 所说:"让每次签入的代码都比签出时干净一点。"