AI 写代码在前三个月确实很爽:需求一句话,自动补全一大段,样板代码几分钟就能拼出来,连单元测试、提交信息、接口文档都能让模型代劳。但三个月后,很多人开始觉得不对劲:代码能跑,却越来越不敢改;AI 生成的东西像一个黑盒,看起来都合理,运行一段时间后才发现边界条件全是洞。这篇文章不劝退 AI 编程,而是要把“爽”转化成一种可持续的工程能力。适合正在使用 Cursor、Copilot、Qwen Code、Codex 等工具的开发者,也适合团队里负责技术规范、代码审查、质量保障的人。读完后,你会得到一套从工具链、提示词、验证闭环到长期维护的完整工作流,避免“爽三个月,维护三年”。
1. 先看清“AI 写代码三个月”到底发生了什么
1.1 前期为什么爽
AI 辅助编程刚刚切入日常开发时,收益非常直观。大量重复性劳动被压缩,常见的 CRUD 接口、DTO 定义、配置类、测试桩代码,几乎可以一键生成。IDE 插件还会在输入过程中给出整行补全,减少翻文档、查 API 的时间。这等于把“从零开始写”变成了“从半成品开始改”。
短期体验好的原因有三个:一是任务足够小,AI 在单文件、单函数场景下表现稳定;二是错误被编译器和测试拦住,AI 输出的问题不会立即暴露;三是新鲜感会让人更愿意尝试各种提示词,投入度高,产出自然多。
这个阶段最常见的状态是:一个需求下来,先打开 AI 对话框,描述需求,复制代码,运行通过,然后提交。整个链路看起来高效,但问题也在悄悄积累。
1.2 三个月后的典型症状
三个月是一个分水岭。项目的代码量变大,模块之间的依赖变多,AI 生成的代码不再只影响一个文件了。这时常见的症状会集中出现:
| 症状 | 表现 | 深层原因 |
|---|---|---|
| 不敢改代码 | 改了 A 模块,B 模块的 AI 生成代码跟着坏 | 没有理解生成代码的内部依赖,也没有测试保护 |
| 风格混乱 | 不同文件命名风格、异常处理方式完全不同 | 每次对话上下文独立,AI 不知道全局约定 |
| 版本错位 | 按旧知识生成代码,依赖 API 已废弃 | 模型训练数据有滞后性,项目依赖版本没有被约束 |
| 边界错误 | 正常路径能跑,空值、超时、并发场景出错 | 提示词没有描述边界条件,AI 按“最常见情况”补全 |
| 注释失真 | 注释写得很完整,但实现已经改过 | AI 生成的注释和代码可能来自不同上下文 |
| 上下文丢失 | 前面要求了“不要改鉴权”,后半程还是改了 | 对话太长后,模型弱化或忽略了早期约束 |
这些症状单独看都能解决,但叠加在一起会形成很强的挫败感。尤其是接手他人 AI 生成代码的时候,看不懂当时为什么这样写,也没有设计文档可查。
1.3 问题不是 AI 变笨了,而是工作流没有升级
AI 模型不会在三个月后突然变差。真正变化的是项目复杂度,以及你对代码质量的要求。前期写 Demo、写脚本,错误可以被容忍;后期写业务系统、写核心模块,错误需要被穷尽。此时如果还停留在“复制粘贴、运行通过、立刻提交”的阶段,问题必然爆发。
所以核心判断是:AI 写代码的问题从来不是“写得不够多”,而是“验证不够强、所有权不够清楚、架构约束不够明确”。后续所有方法,都要围绕这三个方向展开。
2. 先搭工具链:AI 编程不是只有聊天框
2.1 常见 AI 编程工具定位
AI 编程工具远不止一个对话框。不同工具适合不同场景,选型之前要先想清楚自己的需求。
| 工具类型 | 代表工具 | 典型场景 | 注意点 |
|---|---|---|---|
| IDE 插件 | GitHub Copilot、通义灵码、CodeGeeX | 代码补全、单文件生成、内联问答 | 对上下文长度敏感,需要配合项目规则文件 |
| 独立编辑器 | Cursor | 多文件编辑、跨文件重构、项目级问答 | 刚上手容易把整个仓库丢给 AI,成本高且易走神 |
| 终端工具 | Codex CLI、Aider | 命令行触发、git 集成、批量任务 | 适合有清晰任务的开发者,不适合探索式开发 |
| 模型 API | Qwen-Coder、DeepSeek-Coder 类模型 | 私有化部署、需要控制敏感数据 | 需要自己处理模型版本、推理资源和评测集 |
| 框架生态 | Spring AI、LangChain 等 | 把模型能力嵌入业务应用 | 本质是应用开发,不是写代码辅助工具 |
选型建议:如果你的代码有强合规要求,优先选择支持私有化部署或离线使用的方案;如果你主要写业务代码,优先用 IDE 插件而不是临时网页对话框;如果你需要让模型理解整个仓库,先确认工具是否能读取项目索引,而不是把文件内容全部粘贴进提示词。
2.2 环境准备与依赖对齐
AI 生成代码能不能跑,很大程度上取决于环境是否一致。先执行一组基础检查,避免“我本地能跑、你本地报错”的问题:
git --version node -v npm -v python --version docker --version java -version mvn -version不同项目只保留自己需要的命令。关键是确认:Node、Python、Java 等运行时版本,包管理器版本,以及是否有统一的锁文件。常见的坑是 AI 提示使用“最新版依赖”,但项目实际用的是另一个大版本。在提示词里写清楚Spring Boot 3.2、Python 3.11、Node 20,比让 AI 自己猜可靠得多。
如果项目使用 Docker 开发环境,还要确保Dockerfile和docker-compose.yml与本地版本一致。不要先让 AI 写代码,再回头调环境;要先把环境固定,再让 AI 在这个边界内生成内容。
2.3 仓库和目录规范要提前定好
AI 缺少全局视角,尤其缺少对目录规范和模块边界的理解。没有规范时,它会在utils目录里塞业务逻辑,在没有分层的地方强行分层,造成结构混乱。更合理的做法是在项目根目录放一份团队约定文件,很多 AI 编程工具支持项目级规则文件,例如AGENTS.md或工具对应的规则文件。
一个最小目录结构示例:
project-root/ README.md AGENTS.md docs/ architecture.md src/ main/ test/ scripts/ lint.sh test.sh .gitignore package.json # 或 pom.xml、pyproject.tomlAGENTS.md可以写这些内容:
# 项目约定 - 后端统一使用 Java 17 + Spring Boot 3.2 - 所有对外接口必须有入参校验和错误码 - 禁止在 Service 层直接操作 HttpServletRequest - 单元测试使用 JUnit 5,测试文件放在 src/test/java 下 - 新增依赖需要先在 issue 中说明原因 - 生成代码必须包含关键方法的注释,但注释要描述行为,不要复述代码把这类约定写进文件,AI 在生成代码时才有可能遵守。只靠对话里说一次,超过上下文长度后就会被遗忘。
3. 把需求转成 AI 能执行的任务
3.1 为什么提示词不能是“帮我写个登录”
“帮我写个登录”是典型的模糊指令。AI 会自行决定用 Session 还是 JWT,要不要验证码,密码怎么加密,是否要刷新令牌,用户表字段怎么设计。这些决定看起来都合理,但未必符合你的项目约束。更麻烦的是,AI 会把它的默认假设写进代码,让未来维护者误以为是业务要求。
更好的方式是把 AI 当成一个能力很强、但完全不了解项目的结对开发者。你需要告诉它角色、任务、已知条件、约束和验收标准。提示词越具体,生成结果越接近可提交状态。
3.2 一个可复用的提示词模板
下面这个模板适用于大多数后端功能生成场景:
角色: 你是熟悉 Java Spring Boot 的资深后端工程师 任务: 实现一个基于 Redis 的接口防重复提交注解 已知条件: - 项目使用 Spring Boot 3.2 - Redis 客户端已经引入 spring-boot-starter-data-redis - 已有基础响应体 Result<T> - 需要指定 key 前缀、过期时间、提示消息 约束: - 不修改已有鉴权逻辑 - 不引入额外依赖 - 使用 AOP 实现,避免侵入业务代码 - 正确处理并发场景,使用 Redis 原子操作 - 生成内容包含: 注解类、切面类、单元测试示例 输出格式: - 每个类单独一个代码块 - 开头用一行说明文件路径 - 关键方法注释说明输入、输出和异常场景把这段提示词发给模型,通常能得到比“写个登录”质量高很多的结果。关键在“约束”部分:它让模型不要越界,也让你后续审查有依据。如果 AI 仍然生成多余代码,可以在提示词末尾追加一句“如果没有必要,不要生成额外类或方法”。
3.3 任务拆分与验收标准
AI 适合处理边界清晰的小任务,不适合一次性生成整个系统。一个常见的拆分原则是:一个提示词只解决一个模块或一个功能点。
| 任务类型 | 是否适合 AI 直接生成 | 人工必须做的事 |
|---|---|---|
| 项目脚手架、目录初始化 | 可以作为参考 | 手动确认依赖版本和文件结构 |
| 单个 CRUD 接口 | 可以生成初版 | 审查权限、校验、事务边界 |
| 复杂状态机 | 不适合直接生成 | 先画状态转移表,再让 AI 生成单步逻辑 |
| 单元测试 | 适合生成基础用例 | 补充边界值和异常场景 |
| 重构已有代码 | 不适合 | 先让 AI 分析代码,提出重构方案,人工确认 |
每个任务都要有验收标准。例如“实现防重复提交注解”的验收标准可以是:同一 key 在指定时间内重复请求返回错误码;不同 key 可以并发通过;单元测试覆盖正常、重复、并发三种情况。把验收标准写进提示词,AI 生成后再逐条验证。
4. 生成之后的验证闭环:不是复制粘贴就能提交
4.1 三道快速检查:编译、静态检查、测试
AI 生成的代码放在编辑器里看起来没问题是常有的事,但提交前至少要过三道检查。不同技术栈命令不同,这里以 Java + Maven 和 Node.js 为例:
# Java / Maven 项目 mvn -q compile mvn -q verify# Node.js 项目 npm run typecheck npm run lint npm test三道检查分别对应三个问题:代码能不能编译;代码是否符合团队风格和静态规则;核心逻辑是否有测试保护。只要有一道失败,就不要提交。
如果你的项目里还没有测试,至少补充一条最核心路径的冒烟测试。对于接口类代码,启动服务后用curl验证真实行为:
curl -X POST http://localhost:8080/api/submit \ -H "Content-Type: application/json" \ -d '{"title":"test"}'正常情况会看到业务成功响应;重复请求时,应看到防重复提交的错误响应。没有运行验证,只靠编译通过,无法发现 Redis Key 设置错误、过期时间失效、事务未生效这类问题。
4.2 代码审查清单
每次提交 AI 生成代码前,按以下清单过一遍:
| 检查点 | 具体做法 |
|---|---|
| API 是否与现有接口一致 | 对比 Controller 路由、请求方法、参数名 |
| 异常是否被吞掉 | 搜索空的 catch 块和printStackTrace |
| 是否重复造轮子 | 搜索项目里是否已有类似工具类或注解 |
| 是否有硬编码密钥 | 检查代码和配置文件中是否出现明文密码、token |
| 是否处理边界输入 | 传入 null、空字符串、超大值、重复请求时是否报错 |
| 是否引入不必要依赖 | 对比 pom.xml、package.json 的变更 |
| 是否符合项目风格 | 命名、日志、错误码、事务注解是否与旧代码一致 |
| 注释是否失真 | 对照注释逐行读实现,不一致就改注释或改代码 |
配合git diff查看变更范围,比直接看 AI 生成的完整文件更有效。提交前运行:
git diff --stat git diff src/main/java/your/module/YourClass.java4.3 提交信息与变更记录
AI 能生成格式标准的提交信息,但它不知道这次提交的真实意图。建议让 AI 生成草稿,然后人工修改:
feat(auth): add idempotent submit annotation for order creation - Add Redis-based idempotent annotation - Add AOP aspect to prevent duplicate submission - Add unit tests for normal and duplicate requests提交时不要git add .,而是按模块逐个添加:
git add src/main/java/com/example/common/annotation/Idempotent.java git add src/main/java/com/example/common/aspect/IdempotentAspect.java git add src/test/java/com/example/common/aspect/IdempotentAspectTest.java git commit -m "feat(idempotent): add order submit duplicate protection"小步提交的好处是:将来定位问题只需看一个提交;如果某个功能不要了,可以精准回滚;Code Review 时也容易盯住一个逻辑单元。
5. 三个月后仍然有效的工程习惯
5.1 代码所有权不能交给 AI
AI 可以生成代码,但无法为代码负责。项目里每一行代码都应该有一个明确的人类 owner。原因是未来一定会有人问:为什么这里要判断expireTime > 0?为什么这个接口不走缓存?如果答案是“AI 生成的”,项目就会陷入无人能解释的困境。
实际操作上,让 AI 生成初稿后,自己至少手动改一遍关键逻辑。手动修改的过程就是理解代码的过程。提交后使用git blame查看每一行归属,如果某个文件的关键行全部来自 AI 工具,说明该文件的技术债风险偏高。
5.2 保持小型可回滚提交
AI 一次性生成的代码经常横跨多个模块,如果整体提交,后续很难定位问题。手动把一个大功能拆成多个提交是一种必要成本。
一个建议的拆分顺序:
- 先提交工具类和公共配置,比如统一响应体、异常码。
- 再提交核心逻辑,比如切面、校验规则。
- 最后提交测试用例和文档。
每步都要保证可以编译、可以运行。不要等全部代码写完再一次性提交,那样既难审查,也难回滚。
5.3 对生成代码做“定期重构”
AI 生成代码在短期可以运行,但不代表长期可维护。两个典型问题:一是重复代码多,因为每个任务都在独立上下文中生成,不会复用之前的实现;二是抽象层次混乱,因为 AI 倾向于把辅助方法放在调用处附近。
建议每完成一个功能,顺手做一轮小重构:提取重复逻辑、统一命名、删掉未使用代码。如果重构范围较大,可以让 AI 先生成重构方案,再由人工执行。例如让 AI 分析一个类是否存在多个职责:
分析 src/main/java/.../OrderService.java 目标: - 找出超过 50 行的方法 - 找出同时处理校验、持久化、发送消息的方法 - 给出拆分建议,但不直接生成代码 约束: - 只输出分析结果,不要修改文件 - 每个建议说明对可测试性的影响这种用法把 AI 从“生成器”变成了“辅助分析器”,对长期维护更友好。
5.4 控制上下文窗口和成本
很多 AI 编程工具按 credits 或 token 计费。把整个仓库粘贴进对话,不仅成本高,效果也差。上下文越长,模型在中间部分出错的可能性越大。
正确做法是每次任务只提供最小相关文件。如果需要让 AI 理解整个项目结构,优先使用支持代码索引、语义检索的工具;如果必须粘贴文件,先删掉无关注释和空行,再把关键类提取出来。
一个实用的操作习惯:每个新功能都开一个新会话。不要把“修改订单接口”和“新增用户列表”放在同一段长对话里。任务结束后主动关闭会话,避免上一个任务的上下文干扰下一个任务。
6. 常见问题与排查路径
6.1 编译报错:找不到符号或缺少依赖
现象:AI 生成代码后,运行mvn compile或npm run build报找不到类、包、方法。
排查顺序:
- 检查是否真的引入了对应依赖,查看
pom.xml或package.json的 diff。 - 检查依赖版本是否和项目其他模块冲突,优先使用项目已有的版本。
- 检查包名是否正确,AI 经常把
com.example写错。 - 检查 JDK 或 Node 版本是否满足库要求。
解决方式:基于项目现有依赖重新生成,或在提示词里明确“只能使用pom.xml中已有的依赖”。
6.2 程序能编译但业务结果不对
这是最隐蔽的一类问题。AI 生成的代码逻辑看起来完整,但真实运行后数据不对、超时、重复提交没有被拦住。
排查链路:
- 先确认输入:请求参数是否真的传进来了。
- 再加日志:在关键判断前后打印参数、缓存状态、返回结果。
- 再写单测:用最小输入固定期望输出,观察哪个分支偏离预期。
- 最后人工修复:不要继续让 AI 扩大修改范围,先定位缺陷,再给提示词。
常见原因是 AI 假设了业务规则。例如防重复提交只判断了 Redis Key 是否存在,但没有设置过期时间;或者缓存判断和业务更新不在同一事务中。这类问题必须通过测试暴露。
6.3 AI 上下文丢失,忘记之前的要求
现象:同一个会话里,前面要求“不要修改鉴权”,后一个请求却把鉴权代码改了。
原因是模型注意力机制天然聚焦最近内容,长对话早期约束容易被忽略。解决方式不是反复提醒,而是把约束外置:
- 把“禁止修改鉴权”写进项目规则文件。
- 把相关测试写进代码,改坏了测试立刻失败。
- 把关键约束写进提示词的最前面和最后面,中间放次要信息。
如果工具支持“项目记忆”,优先使用,而不是依赖对话上下文。
6.4 安全与合规风险
AI 编程工具会把对话内容发送到模型服务端,生产数据、客户信息、私钥绝对不能粘贴进去。即使是使用企业私有化部署,也要遵循最小必要原则。
建议建立团队红线:
| 风险类型 | 禁止行为 | 合规替代 |
|---|---|---|
| 密钥泄露 | 在对话里粘贴数据库密码、API Key | 使用环境变量或密钥管理服务 |
| 生产数据泄露 | 把真实用户手机号、订单数据发给模型 | 用脱敏数据或合成数据 |
| 代码泄露 | 把未公开的商业代码整体发给外部服务 | 使用私有化模型或允许的合规工具 |
| 依赖投毒 | 直接采用 AI 推荐的新依赖 | 核查依赖来源、版本、许可证 |
6.5 提示词调优:输出不满足要求时怎么办
不要继续在原提示词上反复说“不对”,更有效的是给出反面约束。比如:“不要生成 Controller 层,只生成 Service 和测试代码”。或者:“不要使用 Lombok”。一般来说,正面描述“要什么”和反面描述“不要什么”同时出现,效果最好。
如果 AI 生成的代码风格仍然不一致,可以把项目中一个质量较高的文件作为示例粘贴进去,让 AI 模仿现有风格。这比抽象描述“请遵守项目风格”更有效。
7. 可复用的 AI 编程工作流模板
7.1 任务拆解模板
每次使用 AI 写代码前,按下面的顺序走:
- 写需求:一句话说清楚要解决什么问题。
- 拆任务:把需求拆成 30 分钟内能验证的功能点。
- 给输入:补充项目版本、相关文件、现有 API 约定。
- 下约束:禁止做什么、必须做什么、验收标准是什么。
- 生成代码:只让 AI 完成当前子任务。
- 验证:编译、静态检查、单元测试、接口测试。
- 提交:小步提交,写清楚提交信息。
- 重构:顺手消除重复代码,补关键注释。
这套流程可以打印出来贴在工位上,也可以在项目 README 里写一份简化版。
7.2 每次提交前检查清单
| 检查项 | 完成标准 |
|---|---|
| 代码能否编译 | 构建命令无错误 |
| 是否通过静态检查 | lint 或 checkstyle 无新增问题 |
| 是否有测试覆盖 | 至少覆盖正常路径和关键异常路径 |
| 是否阅读过生成代码 | 能说出每个主要方法的职责 |
| 是否排除安全风险 | 无密钥、无生产数据、无未知依赖 |
| 是否拆分提交 | 单个提交只包含一个逻辑变更 |
| 是否可回滚 | 回滚该提交不会影响无关功能 |
| 是否更新文档 | 对外接口变化有说明,规则文件有同步 |
7.3 每周复盘建议
团队或个人都可以做一次轻量复盘,记录这些问题:
- AI 生成代码的返工率是多少?哪些任务需要反复修改。
- 最常见的错误类型是什么?是边界条件、依赖版本,还是项目约定。
- 哪些场景让 AI 写代码反而更慢?以后要不要改成人工。
- 有没有新增 AI 无法理解的业务规则?是否已经写进规则文件。
- 现有测试是否覆盖了 AI 容易出错的地方?不足就补测试。
复盘的目的不是追求“AI 写得越多越好”,而是找到人机协作的最优边界。三个月后还能让你感到“爽”的,不是模型变得更强,而是你已经拥有了一套能让任何代码都保持可维护的流程:验证闭环、代码所有权、小步提交、持续重构。
建议今天就从一个小任务开始,按上面的提示词模板生成代码,再走一遍提交前检查清单。你会发现,真正值钱的不是 AI 生成的代码,而是你能让这些代码稳定运行、随时修改的能力。