接手过三个祖传项目的我,对"维护"二字有着刻骨铭心的理解。最惨痛的一次,一个看似简单的字段加默认值的需求,改完上线后引发了连环NPE,回滚了三次才搞定。那段代码里密密麻麻的if-else嵌套了七层,方法体长达四百行,没有任何注释,变量名是a、b、c这样的天书。
维护老代码的痛,本质上都是前人挖的坑。下面这五个实践建议,是我用无数次加班和故障换来的教训。
一、给变量和方法起个好名字
这听起来像废话,但大部分代码的可读性问题都出在命名上。一个叫process()的方法,鬼知道它处理什么;一个叫data的List,谁知道里面装的是什么数据。好的命名应该是自解释的——不需要注释,看名字就知道意图。
我现在的命名原则很简单:方法名用动词开头,精准描述行为,比如calculateOrderTotal()而不是doCal();布尔变量用is/has/can开头,比如isActive()而不是status();类名用名词,代表一个业务实体,比如PaymentService而不是PayUtil。
花五分钟想一个好名字,能省掉未来五个小时的阅读理解时间。
二、保持方法短小,只做一件事
如果一个方法超过五十行,大概率需要拆分了。我见过最离谱的方法有三百多行,里面同时做了参数校验、数据查询、业务计算、格式转换、日志记录、异常处理……改这种方法的时候,手都在抖。
一个好的方法应该只做一件事,并且把这件事做好。判断标准很简单:如果你没法用一个简洁的句子描述这个方法在做什么,它就该拆。参数校验拆一个方法,核心计算拆一个,结果组装再拆一个。每个方法都短小精悍,上层方法读起来就像在读业务文档:
public OrderResult createOrder(OrderRequest request) { validateParams(request); Order order = buildOrder(request); paymentService.pay(order); return buildResult(order); }这种代码,新来的同事看一遍就能接手。
三、写好日志,给未来的自己留线索
线上出故障的时候,日志就是你唯一的眼睛。我见过太多系统出了问题根本查不了原因——要么没打日志,要么打了一堆无意义的log.info("进入方法xxx")。
我的日志实践是三层规范:入口打请求参数,方便复现问题;关键节点打状态变化,追踪业务流程走向;异常处打完整堆栈,定位错误根源。同时定义好日志级别——ERROR打需要立即处理的错误,WARN打可容忍的异常情况,INFO打业务流程的关键节点,DEBUG打调试信息(生产环境关闭)。
日志打好了,排查问题的时间能缩短80%。
四、封装外部依赖,隔离变化风险
代码难维护的一个重要原因,是外部依赖散落在各个业务方法里。今天用Redis做缓存,明天要换成Caffeine,你得在所有地方改。这既容易遗漏,又容易出错。
正确的做法是加一层抽象。定义一个接口CacheService,业务代码只依赖这个接口,具体实现是Redis还是Caffeine都藏在背后。将来换技术方案,只需要修改实现类,业务代码一行都不用动。这叫依赖倒置,是让代码经得起变化的核心设计原则。
五、写测试,不是为了覆盖率,是为了敢改代码
没有测试的代码,改起来就像拆炸弹——你不知道剪哪根线会爆炸。有一次我为了修一个bug,改了工具类里的一行代码,结果三个完全不相关的功能挂掉了。如果有单元测试覆盖,这种问题在提交代码时就能发现。
测试的真正价值不是跑个覆盖率报告,而是给你"敢改代码"的底气。每次重构完,跑一遍测试,绿色就敢上线。我们不需要追求100%覆盖率,但核心业务逻辑、工具方法、复杂条件分支必须有测试覆盖。
总结下来,易维护的代码并不需要多高深的技术,它只需要做到三件事:读得懂、改得稳、查得快。好的命名和短小的方法解决"读得懂",封装和隔离解决"改得稳",规范日志解决"查得快"。
代码是写给人看的,只是恰好能被机器执行。写代码的时候多想想下一个接手的人——他很可能就是三个月后的你自己。