Java基础、中级、高级、架构面试资料

代码能跑就不要动它

业余杂谈 herman 9浏览
公告:“业余草”微信公众号 AI 中转站提供免费体验,点击链接 https://unity2.ai/register?ref=3XTnndN2 进行访问,支持 Claude、ChatGPT、Gemini 等最新模型!关注业余草微信公众号,添加作者微信:xttblog2!
本博客日IP超过2000,PV 3000 左右,急需赞助商。
极客时间所有课程通过我的二维码购买后返现24元微信红包,请加博主新的微信号:xttblog2,之前的微信号好友位已满,备注:返现
受密码保护的文章请关注“业余草”公众号,回复关键字“0”获得密码
所有面试题(java、前端、数据库、springboot等)一网打尽,请关注文末小程序
视频教程免费领
【腾讯云】1核2G5M轻量应用服务器50元首年,高性价比,助您轻松上云

早前看到有很多大佬分享日消耗几亿 token 重构老系统,不知这些老系统焕发新春之后是否让公司业绩蒸蒸日上了。

AI 能做到重构老系统,但对程序员来说却大不一样。

因为,程序界,有一个不成文的说法,那就是代码能跑就不要动它

这句话老程序员可能都说了二十多年。但很少有人认真追问,到底是什么,把一群热爱技术、追求优雅的程序员,逼成了“能跑就行”的保守派?

接下来,我们就一起来聊聊这背后 6 个没人敢说出口的真相。它们无关技术能力,却决定了键盘上的每一次犹豫。

改出更大问题

当你以为你在修 bug,但也有可能你是在埋雷。

// 这段代码看起来有严重的 NPE 风险
public User getUser(Long id) {
    User user = userDao.findById(id);
    // 为什么不判空?直接返回?
    return user;
}

就拿上面这段代码来说,有人看到这段代码,第一反应是补一个判空。

if (user == null) {
    // 或者抛异常
    return new User(); 
}

就这样,做了改动上线后的第二天,下游系统开始大面积报错。原来下游的一些模块,依赖了 getUser 返回 null 时的特定行为。他们自己做了判空,空的时候走了一套默认填充逻辑。你这一改,下游数据全乱了。

这种事情,在 Java 的世界里也太多了。

  • HashMap 换成 ConcurrentHashMap 想提升并发安全,结果某个角落的代码依赖了 HashMap 的允许 null key 特性,一换就炸。
  • 你顺手把 Date 改成 LocalDateTime,忘了某个序列化框架只认 java.util.Date,反序列化直接失败。
  • 你删掉了一个“明显没用”的私有方法,结果某个框架通过反射在运行时调用它,系统启动直接挂。

你只看到冰山露在水面上的六分之一,你不知道这一点点代码底下,是多少个奇葩逻辑在负负得正地支撑着它

文章配图参见我的公众号:https://mp.weixin.qq.com/s/oLNcpl0qY3C_PbUaTssNQQ

在 Java 这种强类型、重框架的生态里,表面的“坏味道”和深层的“隐性契约”往往是一体两面。你看不懂的那行代码,可能是过去某个凌晨时间,一个即将离职的老哥用命换来的补丁。

改它?你确定你付得起那个学费吗?

修好了是本分,修坏了是事故

这才是职场里最冰冷的算术题,风险不对等。

假设你花了整整一周,把某个 3000 行的 OrderService 拆成了 6 个职责清晰的类,补全了单元测试,消除了 12 个 SonarQube 高危告警。代码质量肉眼可见地提升,Review 时同事纷纷点赞。

然后呢?

没有然后。

业务方不会因为你重构了代码而多给你发奖金,产品经理不会因为你消除了技术债而给你记功,老板甚至不会在周会上提到这件事。在组织的绩效评估体系里,“代码变干净了”是一个不可量化的贡献

但万一,我是说万一,你重构时漏改了一个边界条件,线上出了故障?

那就是另一套算法了。P0 事故、全组复盘、季度绩效扣减、晋升冻结。一封通报批评邮件抄送全公司,标题写着你的名字。

阿里云开发者社区曾经就有不少这类真实故事,导致系统崩溃,客户投诉,全公司通报批评,项目组写检讨。

这类故事的结局不是“下次重构要更小心”,而是整个团队学到的教训变成了:代码能跑,就别改

这不是程序员变懒了,这是组织激励机制的必然结果。当一个系统的奖惩完全不对称时,理性人的最优策略就是“不作为”

代码经济账

让我们算一笔更现实的账。

大家都知道,程序员的精力是有限的,公司的耐心也是有限的。当“改老代码”这件事在绩效体系里没有任何正向反馈时,把同样的时间投入到新功能开发或新技术学习上,ROI 明显更高

更残酷的是,很多公司的技术晋升通道里,“业务影响力”的权重远高于“代码质量”。你重构得再漂亮,评审委员问一句“这带来了什么业务价值”,你就哑火了。

所以不是程序员不想追求优雅,而是追求优雅在这个场景下,是一件“只花钱、不挣钱”的事

代码改完,人被绑架了

这是很多人没意识到的一点,在大多数公司里,“谁改的,谁负责”是一条不成文的铁律

你重构了 PaymentService,把原来的面条代码梳理成了清晰的流程。三个月后,支付渠道接入了一个新需求,出现了偶发的超时问题。排查到最后,发现是新渠道的一个回调格式和重构后的解析逻辑有微妙的冲突。

于是你成了那个“最懂这段代码的人”。

接下来的两年里:

  • 每次支付相关的 bug,@ 的是你;
  • 每次支付需求变更,指派的是你;
  • 每次线上支付告警,半夜被电话叫醒的,还是你。

你改代码的那一周,换来的是未来两年的无限连带责任

在 Java 企业级开发里,这个问题尤其严重。Spring 项目的依赖注入、AOP 代理、事务传播、异步线程池 …… 任何一个环节出问题,排查链条都长得可怕。而重构后的代码,因为“你写的你最熟”,最后一个修改人是你,所以你天然就成了第一责任人。

很多老程序员不是不懂怎么改,而是太懂改完之后会发生什么。他们见过太多“改一行代码,绑定一个人”的案例,所以学会了在动手之前先问自己,我准备好为这行代码的未来两年负责了吗?

如果答案是否定的,那最好的选择就是不动。

没人为不存在的问题买单

面条代码盘根错节,没人会为“不存在的问题”买单,因为它至少是能用的呀。

看上面这个图片,水管虽然断开了,但它至少是能通水的。

大多数 Java 项目里的老代码,虽然存在很多“屎山”,但它能跑。

@Service
public class OrderService {
    @Autowired
    private UserService userService;
    @Autowired
    private InventoryService inventoryService;
    @Autowired
    private PaymentService paymentService;
    @Autowired
    private NotificationService notificationService;
    @Autowired
    private LogService logService;
    // ... 还有 15 个依赖

    @Transactional(rollbackFor = Exception.class)
    public void createOrder(OrderDTO dto) {
        // 这里原本是 200 行,经过 5 年 30 个需求的迭代
        // 现在已经变成了 1800 行
        // 里面有 6 层嵌套 if,4 种异常处理策略
        // 3 套不同的日期格式,以及 2 个"暂时"的硬编码
    }
}

这种代码,你打开文件的第一秒就知道它有问题。但当你真的想动手重构时,你会发现。

  • 它依赖了 20 个 Service,每个 Service 又有自己的依赖网;
  • 它的事务边界里藏着 3 个异步调用,改不好就是数据不一致;
  • 它的异常处理分支覆盖了 7 种历史遗留的兼容场景,你根本分不清哪些是业务必需、哪些是历史包袱;
  • 它涉及的测试用例散落在 4 个不同的模块里,很多已经年久失修。

技术债评估不足,是重构失败的最大原因。你以为这是一座小土丘,挖下去才发现是盘根错节的地下宫殿。

更关键的是,业务方不会为你的“代码洁癖”买单。系统现在跑得稳稳的,订单正常创建,用户正常支付,监控没有告警。在业务视角里,你要解决的是一个“不存在的问题”

没有人付钱让你去优化一个“没有故障”的系统。产品经理的 backlog 里排满了真实的需求,你的重构申请连评审会都进不去。

所以“业务能跑胜过代码坏味道”,这不是技术判断,这是商业组织的生存逻辑

改一行代码,动的是整条产线

这是最容易被低估的一点。

你以为重构只是你和 IDE 之间的事?太天真了。

假设你真的动手改了那个 OrderService,把 1800 行拆成了 6 个领域服务,补了单元测试,消除了坏味道。代码 Review 通过了,CI 也绿了。接下来会发生什么?

  • 测试同学:原来的回归用例全部失效,因为接口行为和入参结构都变了。他们需要重新设计测试场景、补充自动化脚本、做全链路回归。工作量至少一周。
  • 运维同学:原来的监控告警是基于方法级别的,你拆完类之后,调用链变了,Prometheus 的指标采集规则要重新配置,Grafana 的仪表盘要重新画。
  • 实施/交付同学:客户现场的定制化版本里,可能也依赖了原来的类结构。你的改动意味着下一个版本升级时,他们需要重新评估兼容性,写新的升级文档,甚至给客户做培训。
  • 产品经理:他本来计划这个月上线两个新功能,现在因为你的重构占用了测试资源,排期要往后推。他的 OKR 完不成了。

你看,你改的是一行代码,但影响的可能是整条产线上十几个人的工作计划

但你要说,你偷偷的改,改了不让她们知道,不让她们参与。那好,改出问题了,背锅人就只有自己了。

在大型 Java 企业项目里,这种“牵一发而动全身”的效应被放大了无数倍。微服务架构下,一个服务的改动可能涉及上下游 5 个团队的联调;一个数据库字段的变更,可能需要数据团队、BI 团队、运维团队同步调整。

当改代码的社会成本远高于技术成本时,“不动”就成了对整个组织最负责任的选择

不是不想改,是改不起

写到这里,我想大家也应该明白了。

“代码能跑就不要动它”,这句话从来不是技术宣言,它是一代又一代程序员在真实的组织环境、激励机制、风险结构里,用血泪换来的生存策略

它不是真理,因为谁都知道烂代码迟早要还;它也不是借口,因为每个说这句话的人,心里都清楚那团代码有多臭。

它只不过是一个理性的妥协。

凡是说“能跑就行”的地方,其实大家都知道那儿有问题。

那些花几亿 token 重构老系统的大佬,他们比其他程序员多了免责条款呀,还能网上晒着“邀功”呢。

业余草公众号

最后,欢迎关注我的个人微信公众号:业余草(yyucao)!可加作者微信号:xttblog2。备注:“1”,添加博主微信拉你进微信群。备注错误不会同意好友申请。再次感谢您的关注!后续有精彩内容会第一时间发给您!原创文章投稿请发送至532009913@qq.com邮箱。商务合作也可添加作者微信进行联系!

本文原文出处:业余草: » 代码能跑就不要动它