AI写代码三个月后:从效率工具到工程能力的必修课
如果你正在用 AI 写代码,已经写了三到四个月,大概率会碰到一个奇怪的时刻:工具还是那个工具,模型还是那个模型,但它生成的东西越来越“不对味”。半个月前还是神兵利器,现在却要反复修改、删掉重写。不是你的操作方式变了,也不是 AI 变笨了,而是你正在进入 AI 编程真正困难的阶段。
AI 写代码这件事,最容易被误解的地方就在这里:前三个月的“爽”,不是 AI 的上限,而是你的项目复杂度还没到暴露问题的临界点。一旦代码库膨胀、业务规则变多、多人协作开始交叉,AI 生成代码的边际收益会快速下降,甚至会变成负收益。
这篇文章想认真聊一聊:AI 写代码爽过三个月之后,到底会发生什么。不是劝退,也不是无脑吹捧,而是从工程实践的角度,把 AI 编程从“个人效率工具”转向“团队工程能力”这条路上真正要面对的问题、边界和方法梳理一遍。如果你刚开始用 AI 写代码,这篇文章能帮你提前避开几个大坑;如果你已经用了三个月,正在陷入“AI 生成→我不满意→我改说明词→再生成→还是不对”的循环,这篇文章应该能解释为什么。
1. AI 写代码的“三个月定律”
先说一个观察:大量 AI 编程工具的深度用户,都逃不过一个时间尺度,大约三个月。
第一个月,你会用它写脚本、写工具函数、写 demo、写 LeetCode 风格的小算法。这个阶段 AI 表现非常惊艳,因为你给的上下文足够小,任务足够独立。生成结果稍微改改就能跑,甚至直接跑通。
第二个月,你会开始让它接手业务模块:用户管理、订单状态机、支付回调、权限校验。这个阶段你发现它开始“有主见”了,会生成你自己都没想到的边界逻辑,也会在一些不该秀操作的地方秀操作。但整体上还可以接受,因为你心里清楚每个模块的上下文边界。
第三个月,情况开始变化。代码库已经有几万行到十几万行,跨模块的依赖关系变得复杂。你让 AI 改一个接口的实现,它会根据“项目里其他地方的同类代码”推断出一些不存在的约定。你让它新增一个功能,它会用三年前的写法模拟,因为训练数据里那个模式最常见。你开始频繁地跟它复述需求,它频繁地"忘记"你两轮对话前强调过的约束。
这个阶段最典型的情绪是焦虑:不是焦虑 AI 会替代你,而是焦虑自己是不是哪里操作不对,怎么 AI 越用越难用了。
其实你没有做错什么。真正的原因是:AI 编程的瓶颈正在从“能不能生成代码”转移到“能不能正确理解一个复杂系统的现状”。
第一代 AI 编程工具解决的问题是“单点生成”:给我一个函数签名,我帮你填充函数体。它的能力边界在函数和类这个粒度。到了第三个月,你真正需要的不是“单点生成”,而是“跨文件理解”——你需要 AI 知道订单模块的状态机、用户模块的字段定义、支付回调里那个奇怪的重试逻辑、以及上周刚加的灰度开关分别在哪里。这不是 Prompt 能简单解决的,而是需要整个工程链路重新设计。
所以,把前三个月的快感归结为“工具强”,是一个常见的误判。更准确的描述是:前三个月的快感,来源于你把项目复杂度控制在了 AI 的理解能力之内。问题从来不是“AI 能不能写代码”,而是“你所在的代码库复杂度,是否已经超出了 AI 的上下文和推理边界”。
理解这一点,你就明白为什么接下来要聊的那些坑,不是靠换一个更强模型就能绕过去的。
2. 核心问题:AI 把成本转移到了哪个环节
很多团队引入 AI 编程后的第一个季度,会明显感觉到“写代码变快了”。但如果你把视角拉长,用研发全流程来看,会发现成本并没有消失,而是发生了转移。
传统开发模式中,最大的成本集中在“编写”和“调试”。程序员花大量时间敲代码、编译、跑测试、看日志。AI 编程下来之后,“编写”这一环的成本被大幅降低,但“理解”和“验证”的成本被急剧拉高。
过去你写一个函数,写完你会自然知道它做了什么,因为你是从零开始推演出来的。现在 AI 写了一个函数,你需要读懂它的实现,确认它是否符合业务预期,检查它有没有引入隐藏副作用。过去你改一个模块,你会顺带记住模块之间的关系。现在你让 AI 改完一个模块,它不会自动把受影响的两个调用方改掉,也不会主动提醒你缓存策略已经失效。
这就像你以前自己做饭,虽然累,但你知道每道菜放了多少盐。现在你请了个大厨帮你切好菜配好料,你只需要下锅炒。表面上看你省了切菜的时间,但你炒之前得先检查他配的料对不对。遇到怪味还得倒回去查他放了什么。
在软件工程里,这个转移可以用一句话概括:AI 降低了“生产代码”的成本,却没有降低“理解代码”的成本,反而因为生成的代码不是你亲笔写的,理解成本变得更高了。
另一个被忽略的成本是“信任成本”。
当你连续遇到几次 AI 生成了看起来很对、实际有 bug 的代码之后,你会下意识地检查它的每一行输出。这种检查比你自己写代码还累,因为你自己写的时候思路是连续的,而看 AI 写的代码,你是在判读一个陌生人的逻辑。
这个阶段特别容易让团队产生一种错觉:“AI 写代码效率就是不行。” 其实不是不行,而是团队还没有建立起一套适配 AI 编程的“校验流程”。没有校验流程,AI 的输出质量就是不可控的,不可控就意味着你要用更多的注意力去兜底,最终总成本反而上升。
所以,要不要长期用 AI 写代码,本质上不是一道“效率题”,而是一道“工程管控题”。你需要回答的是:你有没有一套机制,让 AI 产出代码的质量可以快速验证,让错误可以被低成本兜住。
如果答案是否定的,那么 AI 写代码的收益就只会停留在“个人玩具”和“原型验证”层面,无法变成团队级的研发效能。
3. AI 写代码不可忽视的安全边界
提到安全边界,很多人第一反应是“AI 生成的代码有没有漏洞”。这当然重要,但更常见、更容易踩到的安全坑,往往不在代码里,而是在 AI 编程工具的“工作方式”里。
第一类风险:代码与数据的隐私泄漏。
现在很多 AI 编程工具默认会把代码内容发到云端模型服务,用来生成补全或修改建议。如果你的项目涉及商业机密、金融数据、未公开的产品策略,又或者你所在的公司有严格的数据合规要求,那么直接把整个代码库开放给 AI 工具,本身就是一次安全事故。
更隐蔽的是,有些 AI 编辑器插件会在后台自动收集光标附近的代码片段,哪怕你没有主动发起 AI 请求,它也在悄悄地工作。你很难感知到,但代码的“上下文”已经被传输出去了。
这里不是要否定云端 AI 编程工具,而是要提醒一个原则:代码片段是否允许出网,应该由公司策略决定,而不是由开发者个人的便利性决定。如果项目处于保密阶段,优先考虑私有化部署模型、使用企业版白名单功能,或者在网络边界上做一层代理,只允许经过脱敏的代码进入模型服务。
第二类风险:生成的代码引入不可控依赖。
AI 模型训练数据里包含了大量真实开源项目的代码,在生成代码时,它会“顺手”引用一些库、函数、API,有时还会给出生僻的依赖坐标。这些依赖可能来自不权威的仓库,可能存在版本冲突,甚至有极小概率被恶意投毒——即攻击者故意在训练数据里埋入带漏洞的代码,诱导 AI 生成并让开发者引入。
从工程实践角度,这里最有效的防线不是靠 AI 厂商自己去过滤,而是要在开发流程里强制加入两件事:依赖来源审查和依赖漏洞扫描。任何由 AI 生成代码引入的新依赖,都必须像人工写的代码一样,进入正常的依赖审查流程。
第三类风险:凭据与密钥。
AI 生成代码时最容易出现的“危险品”是硬编码的 API Key、数据库连接串、第三方服务的 Secret。原因很简单,训练数据里有大量这类样例,模型生成时觉得这是“常见模式”。你如果习惯性地用 AI 生成配置类代码,很可能就在某个配置文件里埋下了一个真实可用的密钥。
所以,凡是由 AI 生成的代码,提交之前必须做一次纵深搜索,检查有没有类似password、secret、api_key、token的字段,以及这些字段的赋值是否来自环境变量。不要把这一步省略掉,因为在 AI 时代,“看到代码就以为它安全”的直觉已经不成立了。
4. 从“代码生成器”到“结对工程师”:工作方式的转变
如果你已经过了三个月的蜜月期,下一步最应该做的,不是寻找“更聪明的补全工具”,而是重新定位 AI 在你工作流中的角色。
很多人把 AI 当成一个“代码生成器”:我提需求,你给我代码,然后就完了。这个模式在简单任务上很快,但一旦进入复杂项目,就成了灾难。因为你把 AI 当成生成器时,你其实是把“需求分析、设计约束、可行性判断”全都压在自己的脑子里,AI 只是你的打字员。当这个打字员疯狂输出之后,你要检查的文字量远远超过手写时代,于是你成了它的校对员。
真正有效的用法,是把它当成一个“结对工程师”。你们不是上下级关系,而是合作者。你要做的不是给它指令,而是跟它一起梳理上下文。
举个例子。你让 AI“给用户中心加一个修改手机号的功能”,这是生成器模式。结对模式则是:先告诉它“用户表中phone字段是唯一索引,修改时需要事务保护,短信验证码校验在SmsCodeService里,历史变更需要记录到UserPhoneChangeLog,并且修改后要清理手机号相关的缓存键”。这样 AI 生成的代码,才是基于项目真实约束的产物,而不是模型训练数据里的“平均味道”代码。
这个转变意味着你的 Prompt 不再是“帮我写个 XX”,而是变成了一个类似“需求文档片段 + 技术约束列表 + 验收标准”的组合。而这个东西,其实比 AI 本身更能决定产出质量。
这里有一个很多人忽略的细节:AI 不懂你的代码库,除非你把它应该知道的东西喂给它。哪怕是最强的模型,如果没有给它数据库表结构、缓存策略、异常处理规范、团队命名风格,它也只能按“通用最佳实践”来生成。通用最佳实践没有错,但它很容易成为你们代码库里第一个风格不一致的“异类”。
所以,从三个月往后,你真正要投入精力打磨的不是“怎么能让 AI 写得更多”,而是“怎么把上下文整理得更清楚”,让 AI 在你限定的边界内发挥。这个技能,业内通常叫“上下文工程”,它是 Prompt Engineering 的下一代形态。
下面是两个可以直接落地的角色转型方案。
4.1 用 AI Assistant 规则文件固定项目上下文
无论你用 Cursor、Cline、Continue 还是其他 AI 编程工具,强烈建议在项目根目录维护一个 AI 规则文件,例如CLAUDE.md或.cursorrules。内容是把项目里“人知道但 AI 不知道”的约定写下来。
# 项目技术栈 - 语言:Java 17 - 框架:Spring Boot 3.2.x - 数据库:MySQL 8.0 - 缓存:Redis,key 统一前缀 `app:{module}:` - ORM:MyBatis-Plus,禁止在循环中单条查询 # 编码约定 - Controller 层只做参数校验和协议转换,不写业务逻辑 - Service 层统一事务边界,方法名使用 doXxx 风格 - DTO 禁止直接暴露数据库实体对象 - 异常统一抛出 BizException,错误码在 `ErrorCodeEnum` 中定义 # 常见约束 - 所有修改尽量兼容 MySQL 5.7+ 的 SQL 语法 - 定时任务必须加 @Scheduled 注释,并说明执行间隔 - 涉及资金或用户状态变更,必须在事务内并补充操作日志 - 不得硬编码配置项,统一走配置中心这个文件的价值在于:它把你团队积累了半年甚至一年的隐性知识,显性化给 AI 看。AI 每次生成代码之前,只要读了这份规则,输出的代码就会更接近团队风格,审查成本会明显下降。
4.2 把需求描述切分成“可验证的单元”
很多人在让 AI 写代码时,喜欢一次性把所有需求都说完,得到一个很大的代码块。这个做法的问题在于,需求越复杂,AI 的推理链越长,出错率越高。更稳妥的方法是把它拆成几个可独立验证的小任务。
任务:修改用户状态更新接口 背景: - 接口路径:PUT /api/v1/users/{userId}/status - 业务规则:用户状态只能从“正常(NORMAL)”切到“禁用(DISABLED)”或“销户(DELETED)”,禁止反向修改 - 必须检查:当前用户是否为管理员,无权限时返回 403 - 必须记录:修改前状态、修改后状态、操作人 ID,写入 user_status_change_log 表 输出要求: 1. 更新 UserStatusService 中的 updateStatus 方法 2. 补充单元测试,覆盖权限不足、非法状态流转、正常流转三条用例 3. 不要修改数据库表结构这种写法的好处是,AI 的任务边界非常清楚,它不会自由发挥去重构整个模块。即使生成结果有问题,你也能快速定位到具体方法,而不是在一个 500 行的“大礼包”代码里找茬。
5. 用工程指标守住 AI 编程的底线
聊了很多理念,现在落到工程实践。无论 AI 写代码的能力多强,只要进入了团队代码库,就必须过工程化这一关。否则,三个月后你得到的不只是慢,而是一坨难以维护、无法上线、甚至没人敢动的历史包袱。
从实际操作层面,建议至少守住下面三条底线。
5.1 没有测试,不允许合并
这一点在 AI 编程时代尤其重要。人工写的代码,提交者至少大概率知道代码要干嘛。AI 写的代码,提交者如果不去读,完全不知道里面发生了什么。唯一的护栏,就是自动化测试。
如果团队还没有可执行的测试基线,请从 AI 生成代码的那一刻开始补齐。比如,你可以要求 AI 在生成业务代码的同时,生成对应的单元测试:
@SpringBootTest class UserStatusServiceTest { @Autowired private UserStatusService userStatusService; @Test void shouldSwitchNormalToDisabled() { User user = new User(); user.setStatus(UserStatus.NORMAL); user.setOperatorId(1001L); userStatusService.updateStatus(user, UserStatus.DISABLED); Assertions.assertEquals(UserStatus.DISABLED, user.getStatus()); } @Test void shouldThrowExceptionWhenStatusFlowIllegal() { User user = new User(); user.setStatus(UserStatus.DISABLED); Assertions.assertThrows(BizException.class, () -> userStatusService.updateStatus(user, UserStatus.NORMAL)); } }这里有个关键原则:AI 生成测试代码的能力,通常比生成业务代码更可靠。因为测试代码的上下文边界比较清晰,适合 AI 发挥。如果 AI 生成的测试覆盖了核心分支,你审查业务实现代码的压力会小很多。
5.2 代码评审必须追问“AI 为什么这么写”
很多团队的 Code Review 还是老一套:看实现是否正确,看有没有明显 bug。在 AI 编程时代,评审的维度应该增加一个:为什么 AI 会选择这个方案?它是否理解和遵循了项目的架构约束?
如果评审时只看了 AI 生成的代码本尊,而不追问它的生成逻辑,很容易出现一种局面:所有代码局部都对,合在一起却不是这个项目的代码。最常见的问题包括:
- 使用了项目里根本不存在的工具类
- 直接在 Controller 里写了事务逻辑,绕开了 Service 层
- 把不该脱敏的字段脱敏了,该加的敏感标签没加
- 生成了完全没必要的分布式锁
- 自行引入了一个“更好用”的 JSON 库,跟团队标准冲突
针对这些问题,评审时可以把“AI 写的代码”当成“实习生写的代码”来审。它很认真,但它不知道你们团队过去踩过的所有坑。评审者的价值,就是把这些坑提前堵上。
5.3 建立“AI 修改审计”机制
如果你的代码量开始大范围由 AI 生成,建议在 Git 提交信息里标注是哪一种来源。比如在 commit message 中增加AI-generated标签,或者在 MR 描述里说明“本次修改由 AI 辅助完成,关键逻辑已人工复核”。
这个机制不是为了甩锅,而是为了建立数据基线。当你可以统计“AI 生成的代码占比”和“由 AI 代码引发的线上问题占比”时,你才能判断 AI 编程对你团队到底有没有带来正向收益。没有数据,你只能靠感觉管理,这在工程上是很危险的。
6. 完整示例:把 AI 编程接入团队开发流程
为了让前面的思路更落地,下面用一个最小化的示例,展示如何把一个 AI 编程任务接入规范流程。
假设业务需求:在订单完成后,给用户发放一张限时优惠券。
传统做法是:产品写需求 → 开发读需求 → 开发写代码 → 开发自测 → 提交评审。
AI 编程的做法可以长这样:
第一步:整理任务上下文
功能背景: - 订单状态变为 COMPLETED 时触发 - 优惠券为满 100 减 10 - 每人每个自然月最多领取 1 张 - 优惠券 30 天内有效 技术约束: - 优惠券服务在 coupon-service 模块 - 消息通过 RocketMQ 异步处理,topic 为 ORDER_COMPLETED - 幂等键使用 orderId + userId 拼接 - 需在 t_coupon_record 表写入发放记录 - 重复发放时返回成功但不执行发放动作 请输出: 1. Consumer 监听逻辑 2. 发放服务的核心方法 3. 幂等校验逻辑 4. 单测覆盖“首次发放”和“重复发放”两个场景第二步:把 AI 生成的代码放入项目,运行测试
这一步的关键是,不要直接复制粘贴。先把代码放在本地分支,然后跑编译、跑测试,看是否通过。
mvn clean compile mvn test -Dtest=CouponConsumerTest第三步:人工评审,重点关注边界条件
AI 生成的代码可能没有考虑到:消息消费失败时的重试次数上限、优惠券过期时间的时区问题、用户已经注销的情况。这些边界不是训练数据里最常见的模式,需要人工把最后一道关。
第四步:提交合并,并在 MR 描述中写清楚 AI 辅助情况
变更说明: - 新增订单完成发券逻辑 - 由 AI 辅助生成核心代码,人工复核并补充边界处理 - 测试覆盖首次发放、重复发放、幂等校验 - 已验证 RocketMQ 消费者幂等性这个流程的最大价值,不是让 AI 自己“跑完全程”,而是把 AI 的产出纳入正常的工程质检体系。AI 可以承担从“需求”到“代码初稿”这部分的效率红利,剩下的审查、验证、边界补全,依然是工程师的核心价值。
7. AI 写代码三个月的常见问题与排查思路
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 之前生成效果很好,现在生成烂代码 | 项目上下文超出模型理解范围 | 查看最近代码库变更,确认是否有大规模重构 | 给 AI 提供更精确的上下文文件,如数据库结构、接口文档;拆分任务粒度 |
| AI 生成了项目里不存在的 API | 模型训练数据中的“常见 API”与项目实际依赖不符 | 检查 import 和依赖坐标 | 在规则文件中固定依赖版本和核心类路径 |
| AI 修改 A 模块,导致 B 模块报错 | 跨模块依赖关系未被模型感知 | 检查 A、B 模块间的编译依赖和调用链 | 让 AI 先输出改动影响范围,再自动执行测试 |
| 用 AI 生成的代码引入安全漏洞 | 训练样本中存在不安全代码模式 | 使用 SAST 工具扫描;搜索硬编码密钥 | 增加安全扫描环节;要求 AI 输出时遵循安全编码约束 |
| 团队大量使用 AI 后代码风格混乱 | 没有统一的项目规则上下文 | 统计不同风格代码比例 | 建立代码规范文档,强制 AI 工具读取;配置统一的 formatter 和 lint 规则 |
| AI 写代码很爽,但改 AI 的代码很痛苦 | 缺乏对 AI 生成代码的理解成本 | 观察需求变更频率和返工率 | 改 prompt 策略,让 AI 生成结构更简单、可读性更强的代码,而不是炫技代码 |
| AI 生成代码后,测试用例全部要自己重写 | 测试输入输出没被 AI 正确理解 | 查看测试失败日志,确认是逻辑错误还是理解偏差 | 给 AI 补充“测试需要覆盖哪些分支”的明确说明 |
8. AI 编程的最佳实践与工程建议
从大量团队实践来看,AI 编程真正生效的前提,不是“AI 有多强”,而是“团队有没有准备好接住 AI 的输出”。下面这些都是可以直接使用的工程建议。
- 维护项目级 AI 上下文文件
每个项目都应该有一个 AI 可读的“项目指南”,内容包含技术栈、模块划分、关键表结构、异常处理规范、命名风格、部署方式、禁止事项。不要让它成为一份“写给人看、AI 不读”的文档,要在 AI 工具配置里确保它会自动加载。
- 把 AI 生成代码纳入常规评审流程
不要因为 AI 生成速度快,就走一套“快速合并”的特殊通道。评审标准应该和人工代码一致:正确性、可维护性、安全隐患、性能、架构一致性、测试覆盖。
- 禁止用 AI 直接操作生产环境
任何涉及生产环境的变更,不管是改配置还是执行脚本,都必须在本地测试环境预演,并经过人工确认。AI 的工具调用能力越强,越要设置准入边界。线上环境的权限,不应该直接暴露给 AI 工具自动执行。
- 对 AI 生成代码做依赖隔离
如果 AI 建议引入一个新依赖,必须确认依赖来源、版本安全性、许可证合规。不要因为“AI 推荐”就盲目添加。
- 建立回归测试基线
AI 代码合入后,最怕的是“本地跑通了,线上炸了”。回归测试是最后防线。如果没有回归测试,至少要跑通核心链路的一键测试脚本。
- 迭代 Prompt 和规则文件
Prompt 不是写一次就完事的。每次遇到 AI 生成质量不佳的情况,回到规则文件里找原因:是没写数据库表结构?没写事务边界?还是没写 API 风格?把这些信息补进去。三个月后,你会发现规则文件本身就是团队最宝贵的资产之一。
- 别用它写你完全不懂的领域代码
这是最容易踩的坑。AI 可以写一个你从未接触过的协议解析器,而且看起来非常专业。但如果出错,你连排查的方向都没有。对于完全陌生的领域,最好先人工理解核心流程,再让 AI 辅助生成细节,而不是把整个模块直接交给 AI。
- 速度不是唯一指标
衡量 AI 编程的效果,不能只看“代码生成的速度”,还要看“代码上线后的问题数”“返工时长”“维护成本”。如果一个 AI 工具让你第一周写代码快了一倍,但三个月后让你排查 bug 的时间翻了两倍,那它在工程上可能是负收益。
9. 从“让 AI 写代码”到“让 AI 写对代码”
回到标题的问题:AI 写代码爽三个月,然后呢?
更准确的回答是:三个月之后,你不能再把 AI 当成“黑盒生成器”来用。你需要开始管理 AI 的上下文、校验它的输出、评估它的影响面、约束它的依赖,把它嵌入到工程流程里。
“让 AI 写代码”只是入门,“让 AI 写对代码”才是真正的工程能力。“写对”包括:符合团队规范、不破坏现有功能、不引入安全漏洞、有测试覆盖、能被后续阅读和维护。
这个过程里最核心的转变,是你从“告诉 AI 做什么”变成“定义 AI 做事的边界”。这个边界不仅是代码级的技术约束,还包括安全边界、权限边界、依赖边界。一个 AI 编程能力强的工程师,本质上是一个能把需求、上下文、约束、验收标准描述得特别清楚的工程师。这种能力,在 AI 出现之前叫“分析设计能力”,在 AI 时代依然存在,只是载体从“写代码”变成了“写上下文”。
最后给一个很实际的建议:如果你现在正处于“AI 写代码爽三个月”的后期,最该做的不是研究更多魔法提示词,而是把这三个月的 AI 生成代码拿出来做个审计。看看哪些代码是稳定可维护的,哪些已经让你看到就想重写。如果大部分都需要重写,不是 AI 的问题,是你在生成之前没有给它足够的“边界”。把边界补上,然后再让 AI 接着写,它会给你一个完全不一样的结果。
