
1. 为什么“让AI写代码”这件事工序比工具更重要很多人第一次用AI写代码脑子里想的都是“我描述一个需求它给我一坨能跑的代码复制粘贴收工”。我一开始也这么想直到有一次让AI帮我写一个带分页、带条件筛选、带导出Excel的后台列表接口它一口气吐了三百多行我贴进IDEA里编译报错十七个字段名对不上、包没导、分页参数顺序反了、导出用的POI版本和我项目里的对不上。那一刻我才意识到问题不在AI写得差而在我把“写代码”当成了一步动作而它其实是一道有先后顺序的工序。这个认知转变很关键。AI编程工具不管是IDE里内置的补全插件还是对话式的代码生成本质上都是一个“高吞吐但需要校准”的生产力工具。它不像编译器那样给你确定的报错也不像搜索引擎那样只给你链接它给你的是“看起来对、但需要你验证”的中间产物。你如果把它当成一个需要走工序的协作对象产出质量会天差地别你如果把它当成许愿池那踩坑就是必然的。我后来复盘发现整个流程其实可以拆成一条很清晰的链路需求收敛 → 上下文准备 → 分步生成 → 本地验证 → 回填修正。这条链路里每一步都有它存在的理由跳过任何一步后面都要加倍还回来。比如跳过“需求收敛”AI就会给你一个“万能但什么都不精”的实现跳过“上下文准备”它就会用一套和你项目完全不搭的技术栈来写跳过“分步生成”它就会把十个功能揉成一坨你连从哪调试都不知道。这篇文章我想把这条工序完整地讲一遍不是讲某个具体工具怎么点按钮而是讲“顺序”背后的逻辑。适合谁看适合已经在用AI辅助写Java、写全栈项目但总觉得“生成的东西能用但不好用”的人也适合刚接触AI编程、还没建立起自己工作流的开发者。我会用Java后端和全栈场景做例子因为这是我踩坑最多的地方但工序本身是跨语言通用的。提示工序不是仪式感它的唯一目的是让AI的产出落在你可控的范围内。任何一步如果让你觉得“多此一举”先想想跳过它之后你要花多少时间擦屁股。2. 需求收敛别让AI替你决定“要做什么”2.1 一句话需求为什么必然翻车我见过太多人这样提问“帮我写一个用户管理模块。”然后AI就开始了它给你建表、写实体、写Mapper、写Service、写Controller甚至还贴心地加了Swagger注解。看起来很完整但你仔细一看字段是它自己编的权限逻辑是它自己想的分页用的是它习惯的PageHelper而你项目里用的是MyBatis-Plus的IPage。这不是AI的错是你给的信息量不足以让它做出和你一致的决定。“用户管理模块”这六个字里藏着至少二十个未决策的点用户有哪些字段手机号要不要唯一索引密码加密用BCrypt还是MD5加盐角色是单角色还是多角色删除是物理删除还是逻辑删除列表查询支持哪些筛选条件导出要不要导入要不要接口返回结构是统一包装还是裸返回这些点你不决定AI就替你决定而它的决定大概率和你项目现有的约定不一致。所以需求收敛这一步的核心动作是把“一句话需求”拆成“决策清单”。我自己的做法是在让AI写任何代码之前先花五分钟列一个表左边是“必须明确的点”右边是“我的决定”。这个表不需要给AI看但它逼着我把模糊需求变成确定需求。2.2 把需求写成“输入-处理-输出”三段式决策清单列完之后我会把需求改写成三段式结构这是我跟AI沟通时最有效的一种格式。以“用户列表查询”为例输入页码pageNum、每页条数pageSize、可选筛选条件用户名模糊、手机号精确、状态精确、创建时间范围处理按创建时间倒序逻辑删除的数据不返回手机号在返回时脱敏中间四位输出统一返回结构{code, msg, data: {total, list}}list中每条包含id、username、phone脱敏、status、createTime你看这样写完之后AI几乎没有发挥空间去“自由创作”它只能按照你给的输入输出去填充实现。这不是限制AI的能力而是把它的能力引导到你真正需要的地方。我实测下来三段式描述能让生成代码的可用率从大概三成提升到七成以上剩下的三成主要是版本兼容和边界条件那些可以在后续步骤里修。2.3 技术栈约束要前置不要事后补还有一个特别容易忽略的点技术栈约束必须在这一步就写清楚不能等AI生成完了你再去改。比如你项目是Spring Boot 2.7 MyBatis-Plus Lombok Hutool你就要在需求里明确写“使用MyBatis-Plus的LambdaQueryWrapper构建查询条件使用Hutool的StrUtil做字符串判断实体类使用Lombok的Data”。你不写AI就可能给你用JPA、用Apache Commons Lang、用原生JDBC拼SQL。我踩过最典型的一个坑是日期处理。我项目里统一用java.time.LocalDateTime结果AI给我生成了java.util.Date还配了一套SimpleDateFormat。这玩意儿在单线程下没问题但在并发场景下SimpleDateFormat是线程不安全的而且和我项目里其他模块的日期类型对不上导致我后面做类型转换做了一下午。从那以后我每次都会在需求里加一句“日期类型统一使用LocalDateTime格式化使用DateTimeFormatter”。注意需求收敛不是让你写一份完整的需求文档而是让你在动手之前把“不确定”变成“确定”。哪怕你只是在脑子里过一遍也比直接让AI开写要强。3. 上下文准备AI不知道你项目里已经有什么3.1 为什么AI总给你“重复造轮子”的代码你有没有遇到过这种情况你让AI写一个工具类它给你写了一个DateUtils里面有formatDate、parseDate、getDayStart一堆方法。你看着挺好贴进项目里结果发现你项目里早就有一个DateUtil而且功能更全。这就是典型的上下文缺失问题——AI不知道你项目里已经有什么它只能假设“什么都没有”然后从零开始造。这个问题在大型项目里尤其致命。你项目里可能已经有统一的异常处理、统一的返回包装、统一的日志切面、统一的权限注解但AI不知道它就会给你生成一套它自己的。你如果直接用了项目里就会出现两套风格完全不同的代码维护起来极其痛苦。所以上下文准备这一步的核心动作是在提问时附带“项目已有资产清单”。这个清单不需要很详细但至少要包括已有的工具类、已有的基类、已有的注解、已有的依赖版本。我通常会这样写“项目已有ResultT统一返回类已有BaseController提供getCurrentUser()方法已有GlobalExceptionHandler处理业务异常请在此基础上实现不要重复定义。”3.2 给AI“看”代码的几种方式上下文准备不只是文字描述更有效的方式是直接给AI看代码。不同的工具支持不同的方式我常用的有三种第一种是粘贴关键代码片段。比如我要让AI写一个Service我会把对应的Mapper接口和实体类贴给它让它知道字段名和已有方法。这种方式最直接但受限于对话窗口的长度适合小范围场景。第二种是利用IDE插件的项目索引能力。有些AI编程插件会索引整个项目你在写代码时它能感知到同包下的其他类。这种方式适合补全场景但生成完整方法时还是需要你手动指定上下文。第三种是维护一个“项目约定”文档。我会在项目根目录放一个AI_CONTEXT.md里面写清楚项目的包结构、命名规范、常用工具类、依赖版本。每次让AI写代码时我把这个文档的内容贴在提问前面。这个做法看起来笨但效果最稳因为它不依赖工具的能力换任何AI都能用。3.3 版本信息是隐形的坑还有一个特别隐蔽的上下文问题依赖版本。你项目里用的是MyBatis-Plus 3.5.3AI训练数据里可能是3.4.x这两个版本之间有些API是有差异的。比如LambdaQueryWrapper的select方法在3.5.x里支持传入SFunction数组但在更早的版本里不支持。AI如果按它记忆里的版本给你写你贴进去就编译不过。我的做法是在上下文准备阶段把pom.xml里关键依赖的版本号列出来直接告诉AI。比如“MyBatis-Plus 3.5.3.1Spring Boot 2.7.18Hutool 5.8.22POI 5.2.3”。这几个版本号一给AI生成的代码兼容性会好很多。特别是POI不同版本之间生成Excel图表的API差异很大你不说版本它给你的代码大概率跑不起来。提示上下文准备花的时间和你后面调试花的时间是成反比的。你在这里多写三行说明后面可能少改三十行代码。4. 分步生成为什么“一次写完”是最差的选择4.1 大块生成带来的三个连锁问题很多人喜欢让AI“一次性把整个模块写完”觉得这样效率高。我一开始也这样后来发现这是效率最低的做法。原因有三个第一错误定位困难。AI一次给你五百行代码里面有十个地方有问题你编译报错只能看到第一个改完再编译看到第二个像剥洋葱一样一层层剥心态很容易崩。而且有些错误是关联的你改了A导致B也变了最后你都不知道原始版本长什么样。第二风格漂移。AI在生成长代码时前半部分可能用了一种写法后半部分又换了另一种写法。比如前面用if (obj ! null)判空后面用Objects.nonNull(obj)再后面又用Optional.ofNullable(obj)。这种不一致在代码审查时非常刺眼。第三无法增量验证。你没法在生成到一半的时候停下来测试只能全部贴进去再跑。如果跑不通你甚至不知道是哪个部分的问题。4.2 按“数据层→逻辑层→接口层”切分我现在的做法是严格按三层切分每次只让AI生成一层验证通过后再生成下一层。以“用户列表查询”为例第一步生成数据层。让AI写Mapper接口方法和对应的XML如果用MyBatis-Plus就用Wrapper不需要XML。这一步的验证方式是写一个单元测试直接调用Mapper方法看能不能查出数据。这一步跑通说明字段名、表名、查询条件都是对的。第二步生成逻辑层。让AI写Service方法入参是筛选条件对象出参是分页结果。这一步的验证方式是写一个Service层的测试Mock掉Mapper或者用真实数据库看返回结构对不对、脱敏逻辑有没有生效。第三步生成接口层。让AI写Controller方法定义请求参数和返回结构。这一步的验证方式是启动项目用Postman或者浏览器直接调接口看返回的JSON格式对不对。这样切分之后每一步的代码量都不大通常几十行AI不容易漂移你也容易验证。而且每一步验证通过后再进行下一步心里有底不会出现“全部写完发现跑不起来”的崩溃感。4.3 每一步都要给“验收标准”分步生成的关键是每一步你都要告诉AI“这一步的验收标准是什么”。比如生成数据层时你可以说“只需要生成Mapper接口方法和对应的Wrapper构建逻辑不需要生成Service和Controller。验收标准是给定筛选条件能正确拼出SQL并返回分页结果。”这个验收标准的作用是让AI知道“边界在哪里”。你不说它就可能顺手把Service也写了然后你又要去删。你说了它就会聚焦在当前这一步产出更精准。我还会在每一步生成后让AI自己解释一下“这段代码的关键逻辑是什么”。这个动作有两个好处一是逼着AI把它的实现思路说出来你能快速判断它有没有理解错二是如果它解释得含糊说明它自己也没想清楚这段代码大概率有问题。4.4 一个具体的分步生成示例假设我要实现“导出用户列表到Excel”我会这样分步第一步让AI写一个UserExportVO类字段和列表查询返回一致但加上ExcelProperty注解假设用EasyExcel。验收标准字段名和注解值对应正确。第二步让AI写Service里的导出方法入参是筛选条件出参是ListUserExportVO。验收标准能根据筛选条件查出数据并转换成VO列表。第三步让AI写Controller里的导出接口返回void通过HttpServletResponse输出Excel流。验收标准调用接口能下载到Excel文件内容正确。第四步让AI写EasyExcel的写操作代码包括表头样式、列宽设置。验收标准下载的Excel表头加粗、列宽自适应。你看四步下来每一步都很小每一步都能独立验证。这比一次性让AI写一个“导出功能”要靠谱得多。而且如果某一步出了问题你只需要重新生成那一步前面的成果不受影响。注意分步生成不是让你把需求拆得越细越好而是按“可独立验证”的粒度来拆。如果一个步骤你没法独立验证说明它还可以再拆。5. 本地验证AI说“没问题”不算数5.1 编译通过只是最低门槛AI生成代码后第一件事是贴进IDEA编译。编译通过只能说明语法没问题、依赖能找到但离“能用”还差得远。我见过太多编译通过但运行报错的代码空指针、类型转换异常、SQL语法错误、事务不生效、循环依赖这些都是编译期发现不了的。所以本地验证要分层次编译 → 单元测试 → 接口测试 → 边界测试。编译通过后先跑单元测试验证核心逻辑单元测试过了再启动项目调接口接口通了再测边界条件比如空参数、超长字符串、特殊字符、并发调用。5.2 用“反向用例”逼出隐藏问题正向用例谁都会测传正常参数看返回对不对。但真正能暴露问题的往往是反向用例。我通常会准备一组“刁钻”的测试数据筛选条件全部为空看是否返回全量数据且分页正确页码传0或负数看是否有兜底处理每页条数传1000看是否有上限限制用户名传一个SQL注入的字符串看是否被正确转义手机号传一个不存在的号码看是否返回空列表而不是报错创建时间范围传反了开始时间大于结束时间看是否有校验这些反向用例AI在生成代码时通常不会主动考虑但它们是生产环境里真实会遇到的。你测出来之后可以把问题反馈给AI让它补上校验逻辑。这个过程本身就是“回填修正”的一部分。5.3 日志和断点是你的朋友本地验证时不要只看返回结果还要看日志和断点。AI生成的代码有时候逻辑是对的但性能有问题比如在循环里查数据库N1问题或者用了全表扫描的SQL。这些问题在数据量小的时候看不出来但上线后就是灾难。我的习惯是在Service方法入口和出口打日志在Mapper方法执行前后打日志看SQL执行了几次、耗时多少。如果发现循环查库就回去让AI改成批量查询。如果发现SQL没有走索引就检查Wrapper的构建逻辑看是不是字段类型不匹配导致索引失效。断点调试也很重要特别是涉及事务、异步、缓存的时候。AI生成的代码有时候事务注解加在了private方法上不生效或者异步方法直接调用了同类方法不生效这些只有通过断点才能发现。5.4 把验证结果反馈给AI的正确姿势验证发现问题后怎么反馈给AI也很关键。不要只说“报错了”要把错误信息、堆栈、你的操作步骤、期望结果和实际结果都贴给它。比如“我调用导出接口时下载的Excel打开报错‘文件格式不正确’。我的操作是先调列表接口确认有数据然后调导出接口浏览器下载了一个文件用Excel打开提示格式错误。期望是能正常打开并看到数据。错误堆栈如下……”这样AI才能准确定位问题。如果你只说“导出报错了”它可能会猜是数据问题、可能是流没关闭、可能是编码问题猜来猜去浪费时间。提示本地验证的目标不是“证明AI写对了”而是“找出AI写错的地方”。心态上把AI当成一个需要你review的初级开发而不是一个不会犯错的专家。6. 回填修正让AI改代码比让它写代码更考验人6.1 修正时要给“最小上下文”发现问题后让AI修正时不要把它之前生成的所有代码都贴回去只贴出问题的那一段加上错误信息和你的期望。比如“下面这段代码在筛选条件为空时抛了空指针请修正。期望是筛选条件为空时不拼接该条件直接查全量。代码……”这样AI的注意力集中在问题上不会因为上下文太长而“跑偏”。我试过把整个类贴回去让它改一个空指针结果它顺手把其他方法也重构了一遍引入了新的问题。6.2 修正后要重新走一遍验证AI修正后的代码不能直接信任要重新走一遍验证流程。而且要注意修正可能会引入新的问题。比如它为了修空指针加了一个if (condition null) return;结果导致筛选条件为空时直接返回空列表而不是查全量。这种“修一个bug引入另一个bug”的情况很常见。所以修正后的验证不仅要验证原来的问题解决了还要验证原来的正向用例没有被破坏。这就是为什么我强调分步生成——每一步的代码量小修正的影响范围也小。6.3 什么时候该放弃AI生成自己动手不是所有问题都值得让AI反复修正。如果一个问题你让AI改了三次还没改对或者它每次改都引入新问题那就果断自己动手。AI擅长的是“从零生成”和“模式化修改”不擅长“理解复杂业务逻辑后的精准修正”。我自己的判断标准是如果这个问题涉及业务规则的微妙判断比如“什么情况下允许删除用户”或者涉及多个模块的交互比如“删除用户时要同时清理关联数据”那AI大概率搞不定自己写更快。如果这个问题是纯技术性的比如“空指针”“类型转换”“SQL语法”那可以让AI多试几次。6.4 把修正结果沉淀成“项目规范”每次修正完我会把问题和解决方案记下来沉淀成项目里的“AI编程避坑清单”。比如AI生成的日期类型默认是java.util.Date需要手动改成LocalDateTimeAI生成的POI代码默认用XSSFWorkbook大数据量时会OOM需要改成SXSSFWorkbookAI生成的MyBatis-Plus Wrapper默认用eq但字段为null时会拼出WHERE column null需要改成eq(condition, column, value)这个清单越积越多后面再让AI写代码时我直接把这个清单贴在上下文里它就会避开这些坑。这比每次出了问题再修要高效得多。7. 工序跑顺之后AI才真正变成生产力7.1 从“能用”到“好用”的转折点我刚开始用AI写代码时心态是“试试看”生成的东西能用就用不能用就自己写。这个阶段AI对我的效率提升很有限甚至有时候因为要改它的代码比自己写还慢。转折点出现在我把工序跑顺之后。需求收敛让我想清楚了要什么上下文准备让AI知道了项目里有什么分步生成让每一步都可控本地验证让问题早发现回填修正让代码越来越贴合项目规范。这五步跑下来AI生成的代码可用率大幅提升我花在“擦屁股”上的时间越来越少。现在我的体感是一个中等复杂度的CRUD接口从需求到跑通用AI辅助大概能省一半时间。省下来的时间主要在两个地方一是样板代码不用手敲了二是单元测试和边界校验AI能帮我生成初稿。但省时间的前提是工序一步都不能省。7.2 不同场景下的工序微调工序不是死的不同场景下可以微调。比如写工具类需求收敛可以简化因为工具类的输入输出很明确上下文准备要重点说明“项目里已有哪些工具类不要重复”分步生成可以合并因为工具类通常就一个方法。写全栈项目工序要更严格。前端和后端的接口约定要在需求收敛阶段就定好否则AI生成的前端请求参数和后端接收参数对不上。我一般会先让AI生成接口文档路径、方法、入参、出参确认无误后再分别生成前后端代码。改老代码上下文准备最重要要把老代码的逻辑、依赖、调用方都贴给AI否则它改出来的东西可能破坏原有功能。分步生成要更细每次只改一个方法改完立刻跑回归测试。写面试题答案这个场景比较特殊需求收敛变成“明确题目考点”上下文准备变成“明确面试官想听什么”分步生成变成“先给结论再给论据”。但核心逻辑是一样的先想清楚要什么再让AI帮你组织语言。7.3 我踩过的最大的一个坑最后分享一个我踩过的最大的坑。有一次我让AI写一个“批量导入用户”的功能需求描述得很简单“读取Excel解析每一行插入数据库。”AI生成了一段代码我编译通过后直接用了。结果上线后发现导入一千条数据要花三分钟而且中间如果有一条数据格式错误整个导入就失败了前面已经插入的数据也不会回滚。问题出在两个地方一是AI用了逐条插入没有用批量插入二是没有加事务也没有做异常处理。这两个问题在需求收敛阶段如果我多想一步就能避免。比如我应该说“批量导入每批500条使用事务任何一条失败则整批回滚并返回失败的行号和原因。”从那以后我在需求收敛阶段会特别关注“性能”和“异常”这两个维度。性能方面要明确数据量级、是否需要批量、是否需要异步异常方面要明确失败策略、回滚策略、错误反馈方式。这两个维度是AI最容易忽略的也是生产环境最容易出问题的。7.4 给刚开始用AI写代码的人的建议如果你刚开始用AI辅助写代码我的建议是先慢后快。一开始严格按工序走哪怕觉得麻烦也走完。走顺了之后你会形成肌肉记忆哪些步骤可以合并、哪些步骤必须保留心里自然有数。不要一上来就追求“一句话生成整个项目”那是营销话术不是工程实践。真实的工程实践是AI帮你写80%的样板代码你花20%的精力做需求收敛、上下文准备和验证修正。这20%才是你的核心价值也是AI替代不了的部分。还有一点不要因为AI能写代码就放弃读代码。AI生成的每一行代码你都要能看懂、能解释、能修改。如果你看不懂那这段代码就是定时炸弹出了问题你连从哪查都不知道。我见过有人用AI生成了一个复杂的Stream操作自己看不懂上线后出了空指针排查了一整天才发现是Stream里的某个中间操作返回了null。工序的本质是让你在享受AI效率的同时保持对代码的掌控力。掌控力在AI就是杠杆掌控力不在AI就是负债。这个道理我一开始没当回事踩了坑之后才明白。