10 年 Java 后端:从写代码到做系统,我的工程化思维进化之路

10 年 Java 后端:从写代码到做系统,我的工程化思维进化之路

> 这一章写给那些工作了 3-5 年,代码写得不错,但总觉得"做项目还是心里没底"的 Java 后端开发者。讲一讲我从"能实现功能"到"能交付系统"的思维转变。

一、问题背景:为什么很多 Java 程序员写了 5 年代码,还是不会"做系统"

面试的时候,很多人能把 HashMap 源码讲得头头是道,能把 Spring 的循环依赖说清楚。

但真到了项目里:

  1. **需求一变,代码就乱**:产品改个需求,改了三天还没改完,越改 bug 越多;

  2. **写的时候觉得没问题,上线就炸**:本地跑一切正常,一到生产,并发一来就挂,数据量一大就慢;

  3. **只看到代码,看不到系统**:关注的是这个类怎么写,那个方法怎么优化,而不是整个系统的稳定性、可维护性、交付效率;

  4. **出了问题才救火,不会提前预防**:大部分时间在修 bug,很少主动去想"这个地方以后会不会出问题"。

这不是技术能力的问题,是**思维方式的问题**。

很多 Java 程序员的成长路径是:学会语言 → 学会框架 → 学会中间件,然后就停住了。但真正的后端开发,核心不是"会用什么技术",而是"怎么用技术把一个系统稳稳地交付出去,并且在未来三年还能维护得动"。

这一章,我把自己 10 年踩过的坑、总结的方法,整理出来。不管你是想进阶高级开发,还是想转架构,这些思路都能用。

二、核心判断:后端开发的核心,是"管理复杂度",不是"炫技"

我刚工作的前三年,痴迷于"技术深度":

  • 这个框架的源码我看过了;

  • 这个算法的时间复杂度我优化到 O(logN) 了;

  • 这个新特性我用上了,代码更优雅了。

但真到了大项目里,这些东西根本救不了你。真正能救你的,是你能不能把一个复杂系统,拆成简单的、可预测的、出了问题能快速定位的模块。

后端开发的本质,是**在有限的时间和资源里,把业务需求转化为稳定、可维护、可扩展的系统**。

这里的几个关键词:

  • **稳定**:上线不炸,出问题能快速恢复;

  • **可维护**:三个月后你自己还能看懂,别人接手也不骂娘;

  • **可扩展**:业务变了,不用推倒重来;

  • **有限时间**:项目不是给你无限时间打磨的,要交付。

想明白这点,你看代码的视角就变了:你不再关心"这行代码写得够不够优雅",而是关心"这个设计,半年后会不会害死我"。

三、方法框架:我的工程化 5 条军规

这些不是什么书上的理论,是我踩了无数坑,熬了无数夜,总结出来的。每一条背后,都是线上事故的血泪。

1. 先做"可观测",再做"功能"

很多人写代码的顺序是:写业务逻辑 → 跑通测试 → 上线。

我的顺序是:写业务逻辑 → 加日志 → 加监控 → 加告警 → 跑通测试 → 上线。

区别在哪里?

前者上线了,出了问题你不知道,知道了也查不出来。后者上线了,出了问题告警先通知你,你打开日志和监控,5 分钟就能定位到问题。

我自己的标准是:**任何一个接口,没有日志和监控,我就不允许它上线**。

日志要打什么?

  • 入参和出参(敏感信息脱敏);

  • 关键节点的耗时;

  • 异常的完整堆栈;

  • 业务状态的变化(比如"订单从待支付变成已支付")。

监控要看什么?

  • QPS、响应时间、错误率;

  • 数据库连接池、线程池的状态;

  • 关键业务指标(比如每天下单成功的数量)。

没有这些,你就是在裸奔。

2. 任何操作,都要考虑"失败了怎么办"

新人写代码,只考虑成功的路径:

```java

public void createOrder(Request request) {

// 1. 校验参数

// 2. 扣库存

// 3. 生成订单

// 4. 发送消息

// 5. 返回成功

}

```

老程序员写代码,每一步都在想失败了怎么办:

  • 校验参数失败,返回什么错误?

  • 扣库存失败,怎么回滚?

  • 生成订单成功了,发消息失败了怎么办?

  • 发消息超时了,是重试还是回滚?

  • 这个方法执行到一半,服务挂了,数据会不会不一致?

我的习惯是:**写任何代码之前,先列出来所有可能失败的情况,以及对应的处理策略**。

常见的失败处理策略:

  • **快速失败**:前置校验,不行就直接返回,别走到后面再炸;

  • **重试**:网络调用、消息发送这种临时性失败,加重试,但要有限制(最多 3 次,指数退避);

  • **幂等**:同一个请求调用多次,结果要一样;

  • **补偿**:真失败了,要有机制能补回来(定时任务、人工核对);

  • **降级**:非核心功能挂了,别影响主流程。

3. 写代码的时候,就要想着"怎么测试"

很多人写代码,写完了丢给测试,测试测出 bug 再改。这样的效率很低。

我写代码的时候,就会想:

  • 这个方法的输入有哪些边界情况?

  • 这个依赖我能不能 Mock 掉?

  • 这个逻辑我能不能写个单元测试,不用启动整个项目就能测?

  • 这个功能我怎么在本地快速验证,不用等测试环境部署。

我的标准是:**核心业务逻辑,没有单元测试覆盖,我就不提交代码**。

不是说要追求 100% 的测试覆盖率,而是你要对核心逻辑有信心。尤其是那些处理钱、处理状态的代码,一定要写测试。

4. 不要"过度设计",但要"预留扩展点"

架构圈有句话叫" premature optimization is the root of all evil"(过早优化是万恶之源),很多人理解错了,觉得"那就怎么简单怎么来"。

我的理解是:**不要为了不确定的未来,做复杂的设计;但要为了可能的变化,预留扩展点**。

比如:

  • 你现在用 MySQL 存数据,不用一上来就分库分表,但要把数据访问层抽象好,以后真要分的时候不用改业务代码;

  • 你现在用 Redis 做缓存,不用一上来就搞多级缓存,但要把缓存逻辑封装好,以后换实现方便;

  • 你现在只有一个支付渠道,不用一上来就做支付网关,但要把支付接口抽象好,以后加第二个渠道的时候不用改主流程。

怎么判断要不要预留扩展点?我有个简单的标准:**这个需求半年内会不会变?如果大概率会变,就预留;如果不确定,先写死,以后再重构**。

5. 代码是写给人看的,不是写给机器看的

很多人写代码,追求"一行代码实现复杂功能",觉得这样很厉害。但实际上,这样的代码三个月后你自己都看不懂。

我对代码的要求是:

  • **命名要准确**:别用 `data`、`info`、`process` 这种模糊的名字,叫 `userOrderPaymentRecord`,虽然长,但一看就知道是什么;

  • **方法要短小**:一个方法超过 50 行,我就想拆;

  • **注释要讲"为什么",不是"是什么"**:别写 `// 扣库存`,要写 `// 这里用乐观锁扣库存,因为并发不高,不需要分布式锁`;

  • **业务逻辑要集中**:别把同一个业务的逻辑散落在 10 个地方。

代码的可读性,直接决定了维护成本。你今天少打了几个字,明天可能要花几个小时去理解。

四、落地步骤:从今天开始,你就能做的 5 件事

说再多道理,不如从一件小事开始做。我给你列了 5 个具体的动作,今天写代码的时候就能用上。

第一步:给你接下来要写的接口,加上完整的日志和监控

不用搞复杂的 AOP 或者什么高大上的东西,就在你的 Controller 或者 Service 里,手动把关键信息打出来:

```java

log.info("createOrder start, userId:{}, goodsId:{}, amount:{}", userId, goodsId, amount);

long start = System.currentTimeMillis();

try {

// 业务逻辑

log.info("createOrder success, orderId:{}, cost:{}ms", orderId, System.currentTimeMillis() - start);

} catch (Exception e) {

log.error("createOrder failed, userId:{}, goodsId:{}", userId, goodsId, e);

throw e;

}

```

就这么简单,出了问题你至少能看到是哪个用户、哪个商品、花了多长时间、报了什么错。

第二步:写代码之前,先列出来 3 个可能失败的场景

比如你要写一个"用户下单"的接口,先想:

  1. 扣库存失败了怎么办?

  2. 生成订单成功了,发消息失败了怎么办?

  3. 用户重复提交了怎么办?

不用一开始就把所有情况都处理完美,但你要想到这些情况,并且在代码里留下处理的地方,或者至少在注释里写清楚"这个地方暂时没处理,以后要注意"。

第三步:给你的核心业务逻辑,写 3 个单元测试

不用追求全量覆盖,就写 3 个最关键的:

  1. 正常流程的测试;

  2. 参数错误的测试;

  3. 依赖失败的测试。

能写单元测试的代码,本身就是解耦的、好维护的。

第四步:Code Review 的时候,先看"可读性",再看"正确性"

我做 Code Review 的顺序是:

  1. 命名能不能看懂?

  2. 逻辑是不是集中?

  3. 有没有注释说明"为什么这么做"?

  4. 边界情况有没有处理?

  5. 逻辑是不是正确?

很多人反过来,先看逻辑对不对。但实际上,逻辑错了容易改,代码写得烂了,以后改都没法改。

第五步:每做完一个项目,复盘 3 个问题

项目上线后,不管成不成功,都花一个小时复盘:

  1. 这个项目里,我踩了哪些本来可以避免的坑?

  2. 哪些设计做得好,以后可以复用?

  3. 如果再做一次,我会在哪里多花时间?

把这些记下来,你成长的速度会比只埋头写代码快得多。

五、常见坑:后端开发最容易犯的几个错误

这些坑我都踩过,有些还踩了不止一次。

1. 为了用新技术而用新技术

看到 Redis 集群就想上,看到微服务就想拆,看到新框架就想试。结果就是项目复杂度陡增,你花了很多时间研究新技术,业务进度还慢了。

我的原则是:**能用成熟技术解决的,就不用新技术**。新技术的坑你不知道,踩了就是大事故。

2. 追求"完美设计",导致进度延期

总觉得这个设计还可以更好,那个地方还可以再优化,结果项目延期了。

记住:**能按时交付的够用的设计,好过不能交付的完美设计**。设计是迭代出来的,不是一步到位的。

3. 只关心自己写的那部分代码,不关心整个系统

很多人只关心自己的模块,别人的模块出了问题跟自己没关系。

但实际上,线上出了问题,不管是哪个模块的锅,只要影响了业务,你都要能定位到。你对整个系统的理解越深,解决问题的能力就越强。

4. 不写文档,不做沉淀

觉得"代码就是文档"。但实际上,三个月后你自己都不记得当时为什么这么写了。

我的习惯是:每个复杂的业务逻辑,都在代码旁边写清楚"当时为什么这么设计,有哪些约束,哪些地方是可以优化的"。

5. 不做监控,出了问题才救火

觉得"上线了再说,有问题再改"。结果就是凌晨三点被电话叫醒,查半天查不出来。

记住:**监控是你能睡安稳觉的前提**。

六、总结:后端开发的护城河,是"靠谱",不是"会的多"

很多人问我,35+ 的 Java 后端,竞争力在哪里?

我的答案是:**靠谱**。

什么叫靠谱?

  • 交给你的需求,你能评估清楚工作量,按时交付;

  • 你做的系统,上线不炸,出了问题你能快速解决;

  • 你写的代码,别人接手不骂娘;

  • 遇到复杂问题,你能给出可行的方案。

这些东西,比你会 10 种框架、读过 100 篇源码,有价值得多。

年轻的时候,你可以拼学习能力,拼体力,拼加班。但年纪大了,你要拼的是"让人放心"。

客户愿意把项目交给你,老板愿意把重要的系统交给你,不是因为你技术最牛,而是因为你靠谱,你做的东西稳。

这就是 35+ 程序员的核心竞争力。

行动清单:如果你也想提升工程化能力,可以从这几步开始

  1. 今天写代码的时候,给你的接口加上完整的日志,包括入参、出参、耗时、异常;

  2. 找一个你最近写的方法,列出来 3 个可能失败的场景,加上对应的处理或者注释;

  3. 给你的核心业务逻辑,写 3 个单元测试;

  4. 下次 Code Review 的时候,先看可读性,再看正确性;

  5. 这个周末,花一个小时,复盘一下你最近做的项目,写下来你踩过的 3 个坑。

不用一下子变成架构师,就从这 5 件小事开始做,你会发现,做项目的感觉越来越稳。