ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Java开发进阶:从写代码到做系统,工程思维是分水岭

Java开发进阶:从写代码到做系统,工程思维是分水岭 1. 三年Java经验为何还被困在“能跑就行”的层次先抛一个我在技术社群里见过无数次的场景有朋友工作三年Spring Boot用得滚瓜烂熟八股文背得比面试官还溜JVM调优参数随口就能说出一串但真让他独立负责一个订单系统、一个会员体系、一个库存服务他就开始手足无措——表结构设计靠拍脑袋接口命名随心情代码写到哪里算哪里最后系统是跑起来了但上线后三天两头出问题改一个功能牵一发动全身。这不是个例而是相当一部分三年左右Java开发者的共同困境。问题出在哪语法不够熟框架用得不够溜都不是。真正的分水岭在于你是在“写代码”还是在“做系统”。这两者之间隔着的就是工程思维。先说说什么叫“像样的系统”。在我看来它至少要满足三个特征能在真实业务压力下稳定运行而不是在本地跑通几个接口就万事大吉能被人接手和维护换一个人来看代码、改需求不会想骂娘能持续演进业务变了、流量涨了系统还能在这个基础上扩展而不是推倒重来。你回想一下自己写过的项目有多少是奔着这三个目标去的大部分情况下我们做的是“完成功能”——需求来了建表、写Mapper、写Service、写Controller、联调、上线然后就觉得完事了。至于这个功能会不会把别人写的功能搞挂、会不会在某个边界条件下暴雷、数据库字段这么设计以后扩展怎么办——这些问题要么没想过要么想了但没当回事。而你之所以“没当回事”正是因为你缺少一套工程思维的框架来约束自己。我不是说三年经验的开发者水平不行。恰恰相反三年是一个特别关键的节点基础语法已经烂熟常用框架已经顺手CRUD已经提不起兴致这时候最需要突破的不是技术广度而是思考层次。如果这个阶段还停留在“怎么把代码写出来”的层面那再写三年大概率还是老样子。这篇文章我就想聊聊从“写代码”到“做系统”到底差在哪几步工程思维具体指什么以及怎么在日常开发里有意识地培养它。2. 代码思维与工程思维的五个分水岭很多初学者觉得工程思维是个很虚的东西看不见摸不着。其实不然它体现在非常具体的决策场景里。我总结了自己观察到的五个常见分水岭你对照一下自己平时的做法就能判断自己目前站在哪一边。2.1 拿到需求先想“怎么实现”还是“为什么做”代码思维的人拿到需求第一反应是这个功能用什么技术实现用Redis缓存还是本地内存用同步接口还是MQ异步这些当然要想但如果这是你的第一个念头顺序就错了。工程思维的第一步是先搞清楚需求背后的真实目的。产品说要做一个“签到功能”你以为就是建一张签到表、写一个签到接口你得先问这个签到的目的是拉活跃还是做用户激励要不要连续签到奖励漏签能不能补签这些规则背后的业务逻辑是什么我见过太多人需求都没完全理解就开干结果做完发现产品经理想要的跟自己理解的完全是两回事。返工不是最可怕的最可怕的是你基于错误的理解设计了一套表结构改起来伤筋动骨。所以工程思维的第一课是在动手之前先把问题定义清楚。多问几个为什么多确认几个边界场景这不会让你显得很菜反而会让别人觉得你靠谱。2.2 写代码是“能用就行”还是“想着明天还能改”代码思维的人写代码目标是让功能能跑工程思维的人写代码目标是让功能“以后还好改”。举个最简单的例子。你写一个支付回调接口如果只是业务需要即时处理那你直接在Controller里写逻辑就行。但如果这个系统还要继续迭代支付结果可能有多种类型支付成功、支付失败、部分退款、全额退款每种类型的处理逻辑各不相同那你就该考虑用策略模式把不同支付状态的处理逻辑解耦开——而不是写一个巨大的if-else链。再比如命名。我见过有人写String s1、int num2这种变量名也见过ListMapString, Object list这种泛型擦除式的写法。代码是写给人看的顺便给机器执行。如果两个月后你自己都看不懂自己写的代码那你留给同事的就是一个深坑。工程思维下的代码追求的是可读性、可扩展性、可测试性。这三个“性”不是口号而是具体的编码规范函数不要太长、职责要单一、边界要处理、命名要达意、关键逻辑要有注释。2.3 数据表设计是“够用就行”还是“考虑三年后的变化”数据库表结构设计是最能暴露一个人是代码思维还是工程思维的地方。代码思维的人设计表完全按照当前需求来用户表就放用户的基本字段订单表就放订单的基本字段促销活动需要个类型字段就加个INT至于这个字段以后会不会变成多个类型组合——不管先这样以后再说。工程思维的人设计表会去想这张表未来可能有哪些查询场景哪些字段需要加索引哪些地方会有并发写入业务上可能会有哪些变化导致表结构需要扩展举个例子。设计一个用户积分表代码思维的人可能会直接设计id, user_id, points, create_time四个字段每次积分变动插一条记录。而工程思维的人会想到积分变动需要区分类型签到得积分、消费得积分、活动奖励积分每一种类型可能对应不同的积分策略用户可能需要查询积分明细积分可能有过期时间。于是表结构就会设计成id, user_id, change_type, change_points, total_points, expire_time, remark, create_time并且加上user_id create_time的联合索引。这两种设计短期内都能跑但三个月后业务一变前者的表就得重构后者的表可能只是加个索引的事。2.4 出问题时是“立即修复”还是“定位根因”线上出了问题代码思维的人第一反应是赶紧把数据改回来、把报错修掉、让系统先跑起来。这没错应急处理本来就该这样。但问题解决之后呢代码思维的人到此为止——问题解决了继续干别的。结果同一个问题过两周又出现再修一遍周而复始。工程思维的人会在应急处理之后追问三个问题这个问题的直接原因是什么根本原因是什么我需要在流程上、架构上、代码层面分别做什么改进才能避免同类问题再次发生比如线上有个接口偶尔超时应急方案是重启服务过几天又超时了。如果你只停留在“重启能解决”的层面那你就永远找不到真正的根因——可能是数据库连接池配置太小、可能是慢SQL没优化、可能是某个第三方依赖出现偶发阻塞。只有定位到根因才能把这个问题真正杀死。2.5 交付是“功能完成”还是“完整闭环”代码思维的人认为功能开发完了、测试通过了、上线了这个需求就算交付了。工程思维的人会多走几步有没有完善的监控告警日志有没有打全出问题了能不能快速定位性能有没有压测过数据有没有备份这些东西不是“额外工作”而是系统能长期稳定运行的必需品。没有监控系统挂了都是用户先发现没有日志出了问题无从查起没有压测流量一上来直接雪崩——这些我后面会细说。3. 工程思维的四根支柱需求、架构、质量、演进如果要用一句话给工程思维下定义我会说工程思维是以“长期稳定交付”为目标对需求、架构、质量、演进四个维度进行系统性思考的能力。这四个维度就是工程思维的四个支柱。3.1 需求维度从“做什么”到“为什么做”需求维度要求你不只是需求的执行者还要是需求的理解者和完善者。展开说接到需求后至少要确认四件事这个需求解决了什么业务问题有没有更简单的替代方案核心流程是什么分支流程有哪些异常场景如何处理数据从哪来、到哪去是否需要与其他系统交互性能要求是什么并发量大概多少数据量级多大这些信息很多时候产品经理不会主动告诉你你需要自己去问、去查、去理解。优秀的开发者往往能在需求评审阶段就发现产品逻辑漏洞提出更合理的方案。这种能力从哪里来就是靠平时多问为什么、多想一步养出来的。3.2 架构维度从“代码结构”到“系统结构”架构思维不是架构师的专利每个开发者都应该有一定的架构意识。对这个阶段的开发者来说架构思维体现在三个方面模块划分。这个功能应该放在哪个模块接口应该由哪个服务提供数据应该存在哪个库里边界在哪里这些划分是否清晰直接影响系统的可维护性。扩展预留。当前设计能否平滑支持未来的业务变化比如今天是单机部署明天要水平扩展你的设计方案能不能支持今天积分是整数明天要支持小数点后两位你的字段类型选对了没有故障隔离。一个模块出问题会不会拖垮整个系统依赖的服务挂了你的服务能不能降级缓存挂了数据库能不能扛住这些“万一”的考虑就是架构思维和代码思维的差距。3.3 质量维度从“功能正确”到“持续可控”质量不是测试一个人的事而是整个团队、整个流程的事。工程思维下的质量保障至少包含代码审查别人能不能看懂你的代码逻辑有没有明显漏洞有没有更好的方案自动化测试核心逻辑有没有单元测试关键接口有没有集成测试改动一个功能有没有自动化用例能快速回归监控告警系统关键指标QPS、响应时间、错误率、JVM内存等有没有监控异常有没有告警容量规划日均请求量多少峰值多少当前配置是否能扛住预计多久需要扩容这些工作看起来不是“功能开发”但恰恰是它们决定了一个系统能不能长期稳定运行。很多“上线三个月就重构”的项目都是因为质量这关没过。3.4 演进维度从“当下够用”到“持续演进”最后一个维度是时间维度的思考。你现在写的每一行代码、设计每一张表、做的每一个技术选型都是系统未来演进的起点。演进思维会促使你思考几个问题这个方案在半年后、一年后还成立吗今天的技术选型会不会成为明天重构的障碍这个模块如果要独立成服务现在这种写法方不方便拆分当然演进不是过度设计。你不需要为一个永远不会来的功能做复杂预留但你需要保证系统的可演进性——当变化来临时你有能力平滑地改代码、加字段、接新模块而不是推倒重来。这四个支柱不是独立的它们会同时出现在一个决策中。比如你设计一个接口既要想清楚业务需求需求维度又要选好实现方式架构维度还要保证它可测试、可监控质量维度同时考虑未来如何扩展演进维度。工程思维就是一种同时处理这四个维度的能力。4. 我在真实项目里踩过的“缺工程思维”的坑光讲概念太虚我拿自己亲身经历的几个反面案例展开说说。这些都是实打实的教训不是编的。4.1 一张没有唯一索引的用户表引发的线上事故很多年前我做一个用户系统当时的设计是用户表t_user字段id, username, password, nickname, create_time。username倒是建了唯一索引但业务里有个“手机号登录”的需求我当时想偷懒就没给phone字段建唯一索引而是靠代码判断“先查有没有这个手机号没有再插入”。看起来没毛病对不对逻辑上确实先查后插但问题是高并发下两个请求同时来注册同一个手机号两个都查不到两个都执行插入就会出现两条一模一样的手机号记录。用户再登录时代码就不知道返回哪条了。这个坑本质上就是我没有在“需求维度”和“架构维度”想清楚手机号作为业务上的唯一标识应该由数据库层来保证唯一性而不是靠应用层的先查后插。后来加了个唯一索引问题直接消失。4.2 一个把业务逻辑全部塞进Service的遗留系统我还接过一个遗留系统那个系统的Service层堪称一个“上帝类”——用户相关的所有逻辑包括用户校验、订单查询、积分计算、消息通知全部堆在UserService这一个类里。一个方法几百行里面嵌套着各种if-else完全没法测试。每次改需求都要在那个几百行的方法里找到对应的分支小心翼翼地改生怕动到别的地方。Code Review的时候代码reviewer看着这个类只能无奈地摇摇头。这就是典型的缺少“架构维度”思考的结果。如果当初把用户管理、订单查询、积分计算、消息通知拆成独立的Service每个Service只负责一种业务领域这个系统就不会那么难维护。你说这是技术问题吗不是是工程思维的问题——没有意识到模块划分的重要性。4.3 一个上线三天才被用户发现的日志丢失问题有一次我们上线一个新功能日志也打了但上线后排查问题时发现关键操作的日志根本没记下来。找了好久原因最后发现是日志级别配错了——生产环境的日志级别是ERROR但我们的关键日志用的是INFO级别。你说这是大问题吗不是。但它导致线上问题排查时完全抓瞎只能靠猜。这就是“质量维度”缺失——没有在上线前确认日志级别、日志格式、日志持久化是否弄好了。从那以后我每次上线前都会过一遍关键路径的日志级别对不对异常有没有打堆栈日志文件会不会被撑爆有没有日志采集和检索方案现在你再看这四个坑背后全是工程思维某个维度的缺失。5. 如何有意识地在日常开发中训练工程思维说了这么多理论你肯定想知道怎么落地。我会给你一套可以照着做的清单这些都是我在团队里带着新人一起用的方法。5.1 写代码之前先写一份设计文档很多人觉得写设计文档是浪费时间有那功夫代码都写完了。但你试着这么做过吗动手写代码前先把表结构设计、接口定义、核心流程、异常处理写出来给自己看就行不用很正式。你会发现写到一半就发现很多没有想清楚的问题。比如你要做一个“用户签到”功能写设计文档的时候就会想连续签到中断怎么算补签怎么处理断签之后累计天数要不要清零这些边界条件如果不在设计阶段想清楚写代码的时候就会卡壳或者直接忽略边界条件给线上埋雷。设计文档的格式不重要重要的是强迫自己把思路理清楚。随着经验增加你会发现文档越写越简练因为很多常规的东西已经不需要文字来记录了但一定保留“核心流程设计”和“异常场景梳理”这两个部分。5.2 代码审查时不只是看“对不对”还要看“好不好”Code Review是训练工程思维非常好的场景但要看你怎么参与。如果Review别人代码时只看“这个功能对不对”那你的收获会很有限。你应该带着这几个问题去看这个类、这个方法是否职责单一如果改成需求方这个设计好改吗边界条件有没有考虑并发、超时、重试、幂等这些处理了没有命名是否清晰别人能看懂这段代码是干什么的吗有没有更好的实现方案现在这个方案是不是最优解被Review的时候也是一个好机会。别人给你提的意见不用急着辩解先想想他为什么这么建议他的担心是什么有没有道理。这比你闷头写一百行代码都涨经验。5.3 重构时每步都保证系统处于可用状态重构是锻炼工程思维的好机会因为它要求你有全局视角。一次重构几十个类、改几百个方法风险很大很容易改一个地方挂一片。我推荐“小步重构每步验证”的策略一次只重构一个类、一个方法改完跑一遍测试确保没问题再进入下一个。重构还有一个原则行为不变。就是说重构前后功能表现必须完全一致只是代码结构变了。如果重构过程中发现原来的逻辑有问题先把问题记录下来不要在重构的过程中顺便改逻辑——两件事混在一起出了问题你都不知道怪谁。5.4 多问“如果...怎么办”把异常场景当一等公民这是我个人觉得最重要的一条。写代码的时候把“正常流程”和“异常流程”放在同等重要的位置多问自己几个问题如果这个接口耗时超过5秒怎么办如果Redis挂了这个功能还能用吗如果MQ积压了10万条消息消费者能追上进度吗如果用户重复点了两次提交按钮会不会产生两条订单如果上游服务返回的数据格式变了我们这边能不能优雅降级这些问题想清楚一个就少一个线上事故。别嫌烦工程思维说穿了就是“多做几层假设多考虑几个如果”。5.5 给自己找一位“技术导师”你别觉得写代码这件事只能靠自己摸索。经常回头看看那些比你经验丰富的同事是怎么做技术决策的他们为什么要这样设计表结构、为什么要选这个中间件、为什么这个接口要这么定义。在旁边看着、听着、问着潜移默化里你就能学到很多自己想不到的细节。如果没有这样的同事那就多看优秀开源项目的源码看别人怎么组织代码结构、怎么设计接口、怎么处理边界情况。优秀的设计看多了你自然就知道差在哪了。6. 三年Java之后真正该补的不是技术而是思维回到一开始的问题。为什么写了三年Java依然写不出“像样的系统”我的看法是三年的时间你的Java语法已经够用Spring Boot也够熟CRUD更是闭着眼都能写你缺的不是技术而是把技术串起来解决实际问题的工程思维。正如Java这门外语一样语法只是词汇系统架构才是你要写出的文章工程思维就是组织文章的章法。我见过不少工作三五年的人技术广度很高什么新框架都玩过但让他独立负责一个系统依然手忙脚乱。也见过一些只有两三年经验的人因为平时习惯多想想“为什么”“怎么办”在分析问题时展现出的条理性已经远超他的同龄人。区别就在这里你有没有主动去构建那个思维的骨架。所以我的建议是从今天开始在你下一个需求、下一个模块、下一个接口上主动用前面提到的四个维度去思考问题——需求是什么、架构怎么搭、质量怎么保、未来怎么演进然后把每一步的思考记录下来。半年之后回头看你的成长会很明显的。最后说个我一直在用的方法每写完一个模块我会问自己一句“如果三个月后有一个新需求要在这个模块上做改动我需要改哪些地方”如果答案是需要动很多处说明这个模块的抽象还不够好如果答案只是增加一个分支或者新增一个配置那说明这个模块设计得还可以。这算是我自己用来检验“系统像不像样”的一把小尺子也分享给你。
返回列表