做开发这行久了,谁还没遇到过几座“屎山”呢?
我还记得2018年的那个夏天,刚接手一个三年的老项目。看着那段长达4000行的OrderService类,里面充斥着层层嵌套的if-else,变量名从a1排到了temp_final_2,我内心的“代码洁癖”瞬间爆发。
我觉得自己必须拯救这个项目。于是,趁着那个由于需求不饱和的空档期,我花了整整一周时间,把这个类拆分成了策略模式。自我感觉良好,甚至幻想在周一的晨会上接受团队的膜拜。
结果呢?周一上线,由于我对一个不起眼的边界条件理解错误,导致VIP用户的运费计算全盘出错。客服电话被打爆,老板脸色铁青,我不得不回滚代码,灰溜溜地把那一周的心血全部删掉。
那一刻我才明白:在中小团队,重构不是为了炫技,甚至不是为了“干净”,而是为了活下去。
如果你现在也正对着一堆烂代码感到焦虑、恶心,甚至想推倒重来,请先停下来喝杯茶。作为过来人,我想和你聊聊,如何在不搞垮项目(和自己心态)的前提下,找到最安全的重构切入点。
一、 别碰那些“虽然丑但稳定”的代码
很多时候,我们想重构仅仅是因为它“丑”。
“这段代码逻辑太乱了,完全不符合设计模式。” “这个SQL写得太烂了,怎么能用循环查库呢?”
我曾经有个同事小李,也是个完美主义者。他接手了一个报表导出功能,那个功能代码写得很糟糕,全是硬编码的SQL拼凑。虽然从来没出过错,但他觉得这代码“有毒”,非要用ORM框架重写一遍。
结果,那个报表涉及很多历史脏数据,原来的硬编码SQL恰好兼容了这些脏数据,而严谨的ORM框架直接报错。小李修了三个通宵的Bug,最后项目延期,被产品经理疯狂吐槽。
这里有一个残酷的真相:对于中小团队来说,能跑的代码就是好代码。
如果一段代码写得像意大利面条一样乱,但它已经稳定运行了两年,而且最近半年都没有新需求要改动它,请绝对不要碰它。哪怕它看起来让你很难受,那也是经过岁月洗礼的“防弹代码”。
你需要关注的,不是那些“丑”的地方,而是那些“痛”的地方。
只有当你要修改它来支持新业务,或者它经常报Bug时,才是动手的时机。为了重构而重构,是技术人员最大的陷阱。
二、 盯着“热点区域”,用git log教你做人
既然不能全改,那从哪下手?
我现在的习惯是,每接手一个旧项目,不看架构图,先看git log。
有一个很实用的方法,我称之为**“热点图谱法”**。你可以运行这样一句简单的命令,看看过去半年里,哪些文件被修改的次数最多:
git log --pretty=format: --name-only --since="6 months ago" | sort | uniq -c | sort -rg | head -10
你会发现,80%的需求变更和Bug修复,通常都集中在20%的文件里。
拿我去年带的一个电商项目来说,虽然整个项目有几百个文件,但PaymentController.java这个文件在三个月内被修改了45次。每次改动都会引发冲突,每次上线都提心吊胆。
这就找到了切入点。
因为这个文件是“热点”,说明业务变化快。在这里进行重构,投入产出比最高。我们当时没有重写整个支付模块,而是做了一个很小的动作:
- 把这个大文件里,处理“微信支付”和“支付宝支付”的逻辑,分别提取成了两个独立的小类。
- 原来的大文件只负责调用。
就这样一个简单的动作,后续的维护成本降低了一半。不要试图去清洗整个下水道,你只需要把堵得最厉害的那个弯管换掉。
三、 保护式重构:给“裸奔”的代码穿上铠甲
当你选定了切入点,准备动手时,最大的焦虑通常是:“我改坏了怎么办?”
这种恐惧是正常的。老旧代码通常没有单元测试,逻辑耦合得像团乱麻,改一行代码可能会在看似无关的地方引发雪崩。
我曾遇到过一个计算工资的核心方法,里面有几百行逻辑,完全不敢动。后来,我用了一个笨办法,叫**“黄金样本测试(Golden Master Testing)”**。
具体操作是这样的:
- 我不去理解代码逻辑,而是找来1000条真实的历史输入数据。
- 把这些数据跑一遍现在的旧代码,记录下1000个对应的输出结果,存成一个文件(这就是“黄金样本”)。
- 写一个测试脚本,确保无论我怎么改代码,这1000个输入对应的输出,必须和“黄金样本”一模一样。
有了这个“笨”测试做保底,我才敢大刀阔斧地去提取方法、重命名变量。
有一次,我不小心删掉了一个看似多余的if (user == null)判断,结果测试脚本立马报错。原来在某种极端的历史数据下,user真的会为空。如果没有这个测试,这个问题只有等发工资那天才会暴露,后果不堪设想。
重构的本质是“在不改变外部行为的前提下改善内部结构”。如果没有测试来定义“外部行为”,那你做的不是重构,是赌博。
写在最后
其实,随着年岁增长,我越来越宽容了。
看着那些被前人堆砌起来的“屎山”,我不再感到愤怒,反而有一丝敬畏。因为正是这些看似混乱的代码,支撑了公司几年的业务,养活了现在的团队。
你有没有发现,我们往往高估了一次性重构的能力,而低估了日拱一卒的力量?
如果你正准备对老代码下手,不妨试试这三个微小的行动:
- 别立项: 只要不是架构级变更,别专门立项搞“重构月”,这通常会以烂尾告终。
- 童子军规则: 每次修Bug或做新需求时,顺手把当前函数里的变量名改得可读一点,或者把一小块重复代码提炼出来。就做这么一点点,然后提交。
- 留下测试: 如果你费劲读懂了一块复杂的逻辑,给它补一个单元测试。这是你留给后来人(或者三个月后的自己)最好的礼物。
代码永远不会完美,但只要它今天比昨天清晰了一点点,那就是我们在对抗熵增路上最大的胜利。加油,咱们慢慢来。