AI改写75%代码工作?Java工程师工作流重构实战
最近和一帮做Java的朋友聊了个话题大家的感受高度一致手边的AI代码助手已经从“偶尔尝尝鲜”变成了“每天工作流里离不开的一个角色”。与此同时网上到处流传着一个说法——AI将改写75%的代码工作。第一次看到这个数字时我心里咯噔一下但真把自己手头的工作拆开看才发现它说的不是“75%的人要失业”而是“75%的代码工作方式会变”。这个转变已经在不知不觉中完成了大半。今天这篇东西我不打算谈什么宏大趋势只想从一个还在写Java、还在被线上问题折腾的工程师视角把我看到的、踩过的、验证过的东西摊开来说。你会看到AI具体改写了哪些工作哪些环节依然非人不可以及现在这个节点上一个Java工程师到底应该把精力花在哪。1. 先别慌“75%被改写”到底在改写什么1.1 被改写的是任务结构不是岗位数量很多人一看到“改写75%代码工作”就开始焦虑其实这个数字更应该被理解为在日常开发中大约有七成左右的工作内容是模式化、样板化、可预测的。这些内容恰好是大语言模型最擅长处理的领域。你回想一下自己的一天建实体类、写DTO、配Mapper、写Service层的CRUD、补单元测试、调SQL、写接口文档、给上个世纪的代码补注释。这些工作占了你多少时间我粗略统计过自己过去半年的工时这类“确定性产出”大概占了65%到70%。AI真正改写的是这部分——它把“亲手写”变成了“描述、生成、审查、修正”。这不是岗位消失的逻辑而是任务重分配的逻辑。就像当初IDE出现时没人因为不用手写编译命令而失业反而腾出了手去做更有价值的事。现在AI做的事情本质上和IDE当年做的事情一样只不过粒度更粗、智能程度更高。1.2 一张表看清Java开发任务的可替代性分层我把Java日常开发任务做了个分级你可以对照自己的实际工作看看分层典型任务被AI改写的程度原因高替代层CRUD接口、实体/Mapper/配置类、单元测试、常规SQL、注释文档70%~85%模式固定、上下文明确、有大量历史样例中替代层业务规则实现、异常处理、三方系统对接、参数校验30%~50%需要业务理解但AI能给出可用初稿低替代层架构设计、性能调优、分布式方案、跨团队协调、需求抽象5%~15%依赖经验判断和复杂取舍AI只能辅助注意高替代层恰恰是很多初级和中级工程师的主要工作对象。这解释了为什么有些人觉得AI是神器有些人觉得AI是“高级复制粘贴”——关键不在于AI而在于你被分配到的任务处于哪一层。我在一个老项目上做过一次实验把一个订单模块的9个接口全部用AI从零生成从实体到Mapper XML再到Service实现整个过程只花了一个下午。如果按我五年前的效率至少需要两天。但让人真正冒冷汗的不是效率差而是AI生成的代码里有三个隐含问题如果不经过人审直接上生产迟早出事。这个后面细说。2. AI重构Java工作流的五个核心变化2.1 CRUD从“手写”变成“口述”先说最直观的变化。以前写一个新的REST接口从Controller到Service再到Mapper一套流程走下来手快也要四十分钟手慢一点再调调格式一个小时就没了。而且写出来的东西千篇一律入参校验、调用Service、捕获异常、返回统一包装。现在的模式完全不同。我只需要把需求描述清楚AI直接生成可编译的代码。举个例子我最近在一个模拟项目X里新增一个订单创建接口提示词大概是这样的项目使用Spring Boot 3.2、Java 17、MyBatis-Plus。创建一个REST接口 POST /api/orders入参包含userId、productId、quantity、remark。 要求校验quantity必须大于0且不大于100userId和productId不能为空 使用统一返回体ResultT异常由全局异常处理器兜底 Service层用Transactional创建时自动设置createdTime和orderStatusPENDING。AI生成出来的Controller和Service初稿基本能直接用。我只需要做两件事一是检查它有没有正确处理null入参二是确认事务边界符合业务预期。整个流程从一小时缩减到十五分钟里面至少有十分钟花在审查而不是编写上。2.2 单元测试从“磨洋工”变成“顺手就写”单元测试是Java工程师心里永远的痛。理论上大家都知道要写、要覆盖、要保证质量但业务压力一来测试永远是第一个被牺牲的。我见过太多项目测试覆盖率长期在30%以下甚至有的服务类根本没有测试。AI出现后这个局面彻底变了。以前写一个Service方法要配套四个测试用例从mock依赖到准备数据再到断言结果怎么也得半小时。现在只需要把方法签名和业务规则丢给AI它就能生成一套覆盖正常流程、边界条件和异常路径的测试代码。我自己的习惯是写完一个方法后让AI生成测试类然后我补两条它漏掉的边界用例再跑一遍确认全绿。这个流程的成本从半小时降到了五分钟。测试的意愿度提高直接带来的是重构时的安全感——AI时代代码变更频率会更高没有测试兜底谁都不敢动老代码。2.3 代码审查从“人肉扫雷”变成“人机共检”我以前做过一个不算严谨的统计我经手的代码评审里大概有六成的问题集中在空指针隐患、资源未关闭、异常被吞掉、校验缺失这几类。这几类问题有一个共同特点它们都符合“明显可识别的模式”而AI识别模式的能力恰恰非常强。现在的评审流程变成了这样代码写完先让AI过一遍它会指出潜在的空指针、未关闭的IO流、缺少的边界校验甚至能根据上下文提示事务问题。然后我再基于业务规则做二次审查。AI负责“扫雷”我负责“判断”。这让我能把自己的注意力集中在那些真正需要人脑判断的地方而不是把时间耗在逐行找低级错误上。不过这里要泼一盆冷水AI做代码审查存在一个致命弱点——它倾向于在“代码自洽”的范围内发现问题它很难察觉“代码和业务预期不一致”这类问题。换句话说代码里写错了业务规则但只要代码内部逻辑自洽AI大概率发现不了。所以人机共检的“人”这一环不仅不能省反而更关键。2.4 遗留代码从“不敢动”变成“可对话”Java生态里存量老旧系统实在太多很多跑得好好的老系统用的是十几年前的技术栈。这类代码有共同特征没有注释、命名混乱、方法体五百行起步、牵一发动全身。以前接手这种模块光读懂代码就要一两周理解业务和代码之间的映射关系更是全靠人工脑补。AI在这里的价值是让我可以“和代码对话”。我会直接丢给AI一个老方法让它解释这段逻辑在干什么、有哪些副作用、依赖了哪些外部状态。它给你一份图文并茂的解读比让一个老同事从零开讲要快得多。更重要的是AI能辅助做迁移和重构。我最近在一个遗留项目里把一套基于自研ORM的查询迁移到MyBatis-PlusAI帮我完成了绝大多数SQL语义的等价改写。这个活儿如果靠人来做枯燥且易错AI干这个再合适不过。但注意迁移后的对比测试必须自己补AI在语义边界上偶尔会翻车。2.5 结对节奏写一段停一步审一下很多人刚接触AI编程时容易走两个极端。要么完全不信AI的产出只是把它当成补全工具要么毫无保留地信任AI一键生成后直接往代码库里塞。这两个极端我都见过也都翻过车。前者的问题是效率没提起来除了自动补全AI的价值基本没吃到后者的麻烦更大因为AI生成的代码表面光鲜埋在里面的坑往往要等联调或者上线后才爆发。我现在的节奏是“写一段停一步审一下”让AI生成一小块功能代码自己审查这一块确认理解了每一行之后再进入下一块。这比“生成一个大文件然后整体调试”的效率高得多。因为如果一次性生成几百行出问题时你根本不知道AI在哪个环节的假设和你不一致排查成本反而比手写还高。3. 实操实录AI辅助落地一个订单超时关闭模块3.1 场景设定与提示词设计口说无凭我拿一个真实的开发场景把全过程走一遍。假设现在要在一个订单系统里新增一个“订单超时自动关闭”的定时任务业务规则是这样的订单创建后30分钟内未支付自动把状态改为已关闭每次任务只处理一批避免扫表压力过大。传统写法我要做这些事建一个定时任务类、写Mapper查询、写状态变更SQL、处理事务边界、考虑多实例部署会不会重复执行、补测试。整体耗时大概两到三小时。现在我给AI的提示词是这样的Java 17 Spring Boot 3.2 MyBatis-Plus。 写一个订单超时关闭的定时任务类。 表t_order字段id、user_id、statusPENDING1待支付CLOSED5已关闭、created_time、pay_time。 规则每5分钟扫描一次查出statusPENDING且created_time 当前时间减30分钟的订单 每次最多处理200条把status批量更新为CLOSED记录close_time。 要求使用Scheduled分批处理避免大事务不要在事务内做远程调用 全部用MyBatis-Plus的Wrapper实现不要写自定义XML。AI生成的初稿长这样Component public class OrderTimeoutTask { private final OrderMapper orderMapper; public OrderTimeoutTask(OrderMapper orderMapper) { this.orderMapper orderMapper; } Scheduled(cron 0 */5 * * * ?) public void closeTimeoutOrders() { LocalDateTime deadline LocalDateTime.now().minusMinutes(30); ListOrder timeoutOrders orderMapper.selectList( new LambdaQueryWrapperOrder() .eq(Order::getStatus, OrderStatus.PENDING) .lt(Order::getCreatedTime, deadline) .last(LIMIT 200) ); if (timeoutOrders.isEmpty()) { return; } for (Order order : timeoutOrders) { order.setStatus(OrderStatus.CLOSED); orderMapper.updateById(order); } } }3.2 生成结果的四个审查点这个初稿能跑但直接上生产肯定会出事。我来演示一下人类审查到底要看什么。第一个问题多实例部署会重复执行。定时任务如果部署在两台机器上每台机器都会扫描同一批订单产生重复更新。虽然updateById幂等但浪费查询资源而且可能在日志里制造大量噪音。要么引入分布式锁要么用任务调度平台分配执行节点。这里我选择了最简单的方案给任务加一把基于数据库的分布式锁或者干脆拆到一个单独的执行实例里。AI不会知道你的部署架构这正是需要人来判断的地方。第二个问题循环updateById是挨个更新200条就有200次数据库交互。可以接受但不是最优。更好的方式是汇总ID列表用一条批量更新SQL。MyBatis-Plus里可以用updateBatchById或者直接用自定义SQL。我让AI改成批量更新它给出的修正版本如下public void closeTimeoutOrders() { ListOrder timeoutOrders findTimeoutOrders(); if (timeoutOrders.isEmpty()) { return; } ListLong ids timeoutOrders.stream().map(Order::getId).toList(); LambdaUpdateWrapperOrder updateWrapper new LambdaUpdateWrapper(); updateWrapper.in(Order::getId, ids) .set(Order::getStatus, OrderStatus.CLOSED) .set(Order::getCloseTime, LocalDateTime.now()); orderMapper.update(null, updateWrapper); }第三个问题事务边界。初稿在方法上加了Transactional这在这个场景里其实是个隐患。定时任务加事务意味着如果批量更新中有一条失败整个批次都会回滚。但扫描出的200条订单彼此独立一条失败不应该影响其他199条。合理的方案是不要一个超大事务而是把批量拆分每个小批独立事务。AI默认的“方法级事务”在这里是错的。第四个问题时间边界。created_time 当前时间减30分钟这个条件表面正确但仔细想想如果支付渠道回调有延迟用户在第29分钟发起了支付恰好在第31分钟才回调成功而这个定时任务已经把订单关闭了怎么办这已经超出“代码生成”的范畴属于业务规则设计。最终我们加了一个保护逻辑状态为CLOSED的订单如果后续收到支付成功回调需要有补偿机制把它翻回已支付。这个设计AI给不了你它只会顺着你的描述把代码写出来。3.3 效率对比手写、盲信AI、AI加人审我拿同类需求做了个对照用我们自己团队两个周末的时间做了个不那么严谨的横评方式从零开发耗时代码缺陷数上线后可维护性纯手写2.5~3小时3个左右集中在边界条件中规中矩AI生成直接提交40分钟8个以上其中2个属于生产级隐患较差没人知道AI为什么这么写AI生成人审修正1.5小时1~2个且可发现较好审查过程让工程师理解全貌盲信AI那一列最吓人。速度快但代价全在后面。生成时省下的时间在联调和线上故障排查阶段连本带利还回去了。而“AI加人审”虽然只比盲信多了五十分钟但代码质量和可维护性完全不在一个档次上。3.4 提示词迭代把AI“调教”成你需要的样子第一次生成的代码不满意太正常了。真正有用的技巧是“迭代式对话”而不是反复开新会话。比如我给AI的第一次提示词没有说明“多实例部署”它生成的代码也就没有分布式锁。这时候我不会直接把完整答案重写一遍而是追加一条项目是多实例部署定时任务可能重复执行请给出分布式锁的引入方案。AI会把锁的逻辑补上。再比如它生成的批量更新用了last(LIMIT 200)但如果表里同时有多个批次积压每轮扫描永远处理同一批。正确的做法是每次扫描要跳过已经扫描过的批次或者用主键范围分片。这种偏业务口径的细节我通常会在第二轮提示里补充。给AI反馈时最好的做法是给出“代码解释”的组合这里要改成什么样为什么这么改。AI会把你的偏好记住同一个会话里后面的生成结果会越来越贴合你的风格。4. Java工程师需要更新的四项基本功4.1 会提问写清楚提示词的五个要素如果你只能从这篇文章里带走一个技能那是“把需求翻译成提示词”的能力。这不是什么玄学它本质上是一种精确表达的能力和当年写需求文档的底层能力一模一样。我现在一份合格的提示词至少要包含五个要素技术上下文Java版本、框架版本、ORM方案、项目结构。业务规则状态枚举、时间边界、金额精度。约束条件事务边界、性能要求、并发安全。输入输出示例给一个输入样例和期望输出AI的理解会准确得多。明确禁区不要用XML、不要在循环里查库、不要用过时API。举个例子你写“帮我写一个关闭订单的定时任务”和写“用Spring Boot 3.2 MyBatis-Plus写一个订单超时关闭任务扫描PENDING超过30分钟的订单批量更新为CLOSED每次200条分布式部署要考虑幂等”得到的结果完全不同。差的提示词给不了AI足够的约束它只能在猜你的意图猜错是很正常的。4.2 会验证Trust but VerifyAI生成的代码看起来太像“正确答案”了。它有完整的注释、规范的格式、合理的命名错误因此更容易被隐藏。人对“看着很专业”的内容天然有信任倾向这是最危险的地方。我的验证流程是四步第一步编译第二步跑AI生成的单测第三步自己补两条边界用例第四步在本地用真实数据小范围跑一遍。这套流程下来AI代码里大部分问题都会暴露。我见过太多同事第一步编译过了就直接提交最后被别人审查时被问得哑口无言。有一个被反复验证的经验AI生成的代码里最容易出问题的地方是“边界条件的处理”。比如page1这种边界、空集合、null入参、超长字符串、时间为空。AI写的是“正常路径”你需要补的是“不正常路径”。你的价值不在生成而在验证。4.3 会选型方案决策的价值在上升当代码生成变成低成本的事情工程师的稀缺性就转移到了方案选型上。同一个需求AI能给你三套方案同步接口、异步消息、定时轮询。它不会也不应该替你做选择因为它不知道你的团队规模、运维能力、流量模型和成本预算。我在实际工作中越来越明显地感受到能否在十分钟内判断“这个功能该用数据库事务还是消息队列”“这个查询该走缓存还是走索引”“这个模块该拆微服务还是留在单体里”才是真正拉开差距的地方。这些判断AI给不了你至少现在给不了。换句话说AI把“实现”的成本压得很低但把“决策”的价值抬得很高。架构能力、权衡能力、对业务长期演进的理解这些东西的回报率在这两年里被显著放大了。4.4 会表达把业务翻译成可执行的规格写代码的能力贬值但“把模糊业务需求翻译成精确规格”的能力在升值。因为AI的输入是文本你对业务理解的偏差会直接转化为你给AI的下游语句。业务说不清楚你提示词写得再花哨都没用。我现在带新人要求他们在让AI生成代码之前先写一小段“可执行规格”至少包含输入是什么、输出是什么、异常时怎么办、并发下怎么处理、性能要求是什么。写得清楚的人用AI的效率是写不清的人的三倍以上。这不是AI的问题是沟通抽象层的问题。这一点在跨团队协作时体现得尤为明显。产品经理说“用户付款后订单要关闭”这里至少有三层歧义什么时候算付款成功关闭是逻辑关闭还是数据删除关闭后能不能恢复如果你只是把这个句子原样转述给AI那AI就会用它的“常识”帮你做决定而AI的常识并不是你的业务规则。5. 常见翻车现场与排查实战5.1 幻觉APIAI推荐的库方法根本不存在AI最经典的翻车方式是给你一个看起来合理但实际不存在的API。我在一个项目里让AI写一个Redis缓存工具类它直接用了一个redisTemplate.batchDelete(keys)方法看起来后缀一致、语义合理但实际上在项目使用的版本里根本没有这个方法。编译直接报错新手同事第一反应是去查资料最后发现是这个方法压根是编出来的。排查这类问题有一条捷径不要盲目搜索整个互联网把报错信息原样扔回给AI明确告诉它“这个方法在你的语境里不存在请根据当前项目里的RedisTemplate实际API重写”。通常情况下它能自我修正。如果修正不了就让它先列出当前版本RedisTemplate提供了哪些delete相关方法再选择正确的方案。5.2 老版本习惯Java 8写法被硬塞进Java 17项目AI的训练数据里包含了大量历史代码这导致它在没有明确约束时会默认使用比较保守的写法。比如在一个Java 17项目里它可能会生成new String(request.getBytes(ISO-8859-1), UTF-8)这类远古编码转换代码也可能会推荐用CountDownLatch而不是CompletableFuture。解决这个问题最好的办法是在提示词里直接声明技术版本并且在审查时注意识别“看着眼熟但其实是老套写法”的代码。另一个小技巧让AI在生成代码时“为每一处使用了Java 17特性的地方写注释说明”这能逼它使用新写法。实测下来很管用能显著减少代码风格和新项目脱节的问题。5.3 性能隐患编译通过不代表线上能扛AI生成的代码性能问题是最隐蔽的一类因为它能编译、能跑、功能看起来也对只是会在某个数据量级上突然崩给你看。最常见的是N1查询。我让AI写一个批量查询用户订单数量的功能它在循环里逐个查询数据库100个用户就是100条SQL性能可想而知。这类问题靠人审不容易发现因为代码分散在几层每一层看都挺正常。我的办法是在提示词里明确要求“不允许在循环内访问数据库或远程服务”然后审查时再顺着数据流走一遍。如果项目里接入了链路追踪或慢SQL日志建议顺手开起来AI生成的代码上线后多盯几天SQL日志问题会自己浮出来。5.4 安全红线SQL拼接和密钥硬编码AI在生成SQL时偶尔会写出${status}这种直接拼接的MyBatis写法特别是当你让它“写得简单一点”的时候它就把#{}换成${}了。这种代码一旦上线等于把SQL注入的窗口给用户打开。另外AI生成配置文件时可能在代码里塞一个假的数据库密码或第三方密钥如果没被发现就会被当成真配置带进生产。经验做法是AI生成的代码和配置必须做一次安全专项检查。至少检查三个点所有SQL参数是不是都用参数绑定、有没有密钥和Token出现在代码里、有没有绕过权限校验的接口入口。我一度以为这些是最基本的东西直到在代码里看到AI生成的硬编码密钥才意识到这件事必须写进规范。6. 我的几条实操体会AI进来这两年我自己的技能树构成发生了明显变化。写代码的比例在降低但需求分析、方案设计、代码审查、生产排查的比例在上升。过去那个“写完功能就完事”的时代确实回不去了每一行代码前面的思考、后面的验证变得更加重要。给还在Java这条路上的同行几个小建议。第一把AI当成一个“水平忽高忽低的结对程序员”它能在几分钟内给你一份初稿但你不能省掉自己那十分钟的审查。第二提示词写的不是指令是你对业务的理解业务理解越透彻AI给出的产出就越靠谱。第三趁现在多练一下“从零看清一个系统的能力”——一个人能看清整个系统的结构和瓶颈时AI只是他手里的一个效率工具而不会成为他职业道路上的不确定性。最后分享一个我最近养成的小习惯每让AI生成一段代码之后我会要求它把这段代码里它做的关键假设列出来。这个动作帮我发现了大量隐含的业务歧义。有时候AI列出的假设比业务需求本身写得还清楚。这大概是这个阶段最好的工作模式了。