ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

AI编程避坑指南:从提示词到代码审查的实战经验

AI编程避坑指南:从提示词到代码审查的实战经验

1. 为什么AI编程的坑值得单独拿出来聊

用AI写代码这件事,从最初的新鲜感走到日常依赖,中间隔着的不是技术门槛,而是一连串让人血压升高的瞬间。我大概是从两年前开始把AI编程工具真正纳入日常工作流的,那会儿还只是拿它补全一些重复性的样板代码,后来逐渐发展到让它参与模块设计、接口联调、甚至线上问题排查。用得越多越发现,AI编程这件事本身不复杂,复杂的是人和AI之间的协作方式——你怎么描述需求、怎么判断输出、怎么在它跑偏的时候及时拽回来,这些才是决定效率高低的关键。

这篇文章想聊的就是这些。不是教你哪个工具好用,也不是对比哪家模型更强,而是把我自己在实际项目里踩过的坑、总结出来的应对策略,原原本本摊开来讲。适合谁看?如果你已经开始用AI辅助写代码,但总觉得“好像快了又好像没快”,或者你正准备把AI编程引入团队流程,那这些经验应该能帮你省下不少试错时间。核心关键词就一个:AI编程。围绕它展开的所有细节,都是我用真实项目换来的。

先说一个基本判断:AI编程工具的输出质量,跟你给它的输入质量几乎是线性相关的。很多人觉得AI不好用,十有八九是提示词写得太随意。但提示词只是第一层,后面还有上下文管理、代码审查、边界条件验证、版本控制策略等等,每一层都有坑。下面我按实际工作流的顺序,把这些坑一个个拆开讲。

2. 提示词写不好,后面全是白费功夫

2.1 为什么你的提示词总是“差一点意思”

我见过太多人用AI编程的方式是这样的:打开对话框,敲一句“帮我写一个用户登录功能”,然后等着AI吐出一大段代码。结果要么是框架不对,要么是缺少关键校验,要么是数据库连接方式跟项目完全不搭。然后就开始抱怨AI不行。问题出在哪?出在你给的信息量太少了。

AI编程提示词的核心逻辑是:你描述的场景越具体,AI的输出就越接近可用状态。这跟跟人沟通是一个道理。你让同事“帮我搞个登录”,他肯定要问你用什么框架、数据库是什么、要不要验证码、密码加密方式是什么。AI不会主动问,它只会根据你给的有限信息去猜。猜对了是运气,猜错了是常态。

我自己的做法是,在写提示词之前先问自己几个问题:这个功能的输入是什么、输出是什么、依赖哪些已有模块、有没有特殊的业务规则、异常情况怎么处理。把这几个问题的答案组织成一段话,再交给AI。比如同样是登录功能,我会这样写:“项目是Spring Boot 3.x + MyBatis-Plus,数据库是MySQL 8.0,用户表叫sys_user,字段有id、username、password_hash、status、create_time。需要实现一个登录接口,接收username和password,先校验用户是否存在且status为1,然后用BCrypt验证密码,通过后生成JWT返回。密码错误和用户不存在要返回不同的错误码。”你看,这样写出来的提示词,AI基本一次就能给出八九不离十的代码。

2.2 提示词里的“隐形信息”才是关键

除了显式的技术栈和业务规则,还有一些“隐形信息”是很多人会忽略的。比如代码风格——你希望AI用Lombok还是手写getter/setter?日志用SLF4J还是Log4j2?异常处理是抛出去还是就地捕获?这些细节如果不说明,AI就会按它自己的“默认习惯”来,而这个默认习惯很可能跟你的项目规范冲突。

我吃过一次亏。项目里统一用自定义的BusinessException来包装业务异常,结果AI生成的代码里直接抛了RuntimeException。代码能跑,但异常处理链路全乱了,排查问题的时候多花了好几个小时。后来我在提示词模板里固定加了一段:“异常统一使用com.xxx.exception.BusinessException,错误码从ErrorCode枚举中取。”就这么一句话,省掉了后面无数次手动修改。

还有一个隐形信息是代码的“上下文”。AI编程工具通常只能看到你当前粘贴的代码片段,它不知道你项目里已经有哪些工具类、哪些常量定义、哪些公共方法。所以它经常会重复造轮子,或者用一些跟你项目风格完全不搭的实现方式。解决办法是在提示词里附上相关的接口定义或工具类签名,让AI知道“这些东西已经有了,直接用就行”。

2.3 提示词模板的沉淀比每次现写更高效

刚开始用AI编程的时候,我每次都是现写提示词,后来发现效率太低。同一个类型的任务,比如“写一个CRUD接口”,每次都要重新描述一遍技术栈和规范,太浪费时间。于是我开始沉淀提示词模板。

我的模板大概分这么几块:项目背景(技术栈、框架版本、数据库类型)、代码规范(命名风格、异常处理、日志规范)、当前任务描述(具体要做什么)、相关上下文(已有的接口或工具类)、输出要求(是否需要注释、是否需要单元测试)。每次用的时候,把“当前任务描述”和“相关上下文”替换掉就行,其他部分基本不变。

这个模板我放在一个Markdown文件里,用的时候直接复制粘贴。实测下来,光是这一项,每天就能省下至少二十分钟的提示词编写时间。而且因为模板里的规范是固定的,AI输出的代码风格也稳定了很多,后期review的成本明显下降。

提示:提示词模板不要写得太长,控制在500字以内。太长了AI反而会抓不住重点,而且每次粘贴也麻烦。把最核心的约束条件放进去就行,细节可以在对话中逐步补充。

3. 代码审查这一关,AI靠不住只能靠自己

3.1 AI生成的代码为什么不能直接信

AI编程工具最危险的地方在于,它生成的代码看起来往往很“像那么回事”。命名规范、结构清晰、注释齐全,乍一看没什么问题。但只要你仔细读一遍,就会发现各种隐蔽的坑。我总结下来,AI代码的问题大概分这么几类:

第一类是边界条件缺失。比如一个分页查询,AI会写limit offset,但不会处理pageSize为0或负数的情况。一个字符串处理,AI会写substring,但不会判断字符串长度是否足够。这些边界条件在正常流程下不会触发,但一旦遇到异常输入,就是线上事故。

第二类是并发安全问题。AI生成的代码默认是单线程思维,不会考虑并发场景。比如一个简单的计数器,AI可能直接写count++,但在多线程环境下这就是bug。还有HashMap、SimpleDateFormat这些非线程安全的类,AI用起来毫无顾忌。

第三类是性能隐患。AI有时候会写出N+1查询的代码,或者在循环里做数据库操作,或者用全表扫描的方式实现一个本可以用索引解决的查询。这些代码在测试环境数据量小的时候跑得飞快,一到生产环境就原形毕露。

第四类是安全漏洞。SQL注入、XSS、敏感信息硬编码,这些问题在AI生成的代码里出现的频率不低。尤其是当你的提示词里没有明确要求参数化查询的时候,AI很可能直接拼SQL字符串。

3.2 我的代码审查清单

经过多次踩坑之后,我整理了一份AI代码审查清单。每次AI生成代码之后,我都会按这个清单过一遍。清单不长,但覆盖了最常见的几类问题:

  • 所有外部输入是否都做了校验(null、空字符串、长度、格式、范围)
  • 数据库操作是否都用了参数化查询
  • 集合类是否考虑了线程安全(多线程场景下)
  • 循环体内是否有数据库操作或远程调用
  • 异常处理是否完整(有没有吞异常、有没有区分业务异常和系统异常)
  • 日志是否打印了关键参数(但不能打印敏感信息)
  • 是否有硬编码的配置项(IP、端口、密钥等)
  • 资源是否确保释放(流、连接、锁)

这份清单我放在IDE的代码模板里,review的时候直接对照着看。刚开始会觉得麻烦,但养成习惯之后,基本上扫一眼就能发现大部分问题。实测下来,经过清单审查的AI代码,线上出问题的概率能降低八成以上。

3.3 单元测试是AI代码的“照妖镜”

除了人工审查,单元测试是验证AI代码质量最有效的手段。我的做法是,让AI生成业务代码的同时,也让它生成对应的单元测试。但这里有个坑:AI生成的单元测试往往只覆盖正常流程,边界条件和异常场景基本不写。

所以我的策略是,AI生成测试骨架,我来补充边界用例。具体来说,AI生成的测试通常包含“正常返回”的用例,我会在此基础上补充:参数为null、参数为空、参数超长、参数格式错误、依赖服务超时、依赖服务返回异常、并发调用等场景。这些用例写起来不复杂,但能暴露出AI代码里绝大部分隐藏问题。

还有一个技巧是,用AI来生成测试数据。比如需要一个包含各种特殊字符的字符串列表,或者一个边界值附近的数字集合,直接让AI生成比手写快得多。但生成之后要自己检查一遍,确保数据确实覆盖了你想测试的场景。

注意:不要完全信任AI生成的单元测试。我遇到过AI生成的测试用例本身就有逻辑错误,比如断言条件写反了,或者mock的返回值跟实际场景不符。测试代码也是代码,同样需要review。

4. 上下文管理:AI的“记忆”没你想的那么好

4.1 长对话中的信息衰减问题

用AI编程工具的时候,很多人喜欢在一个对话里连续聊很多轮,觉得这样AI能记住前面的内容。但实际上,随着对话轮次增加,AI对早期信息的“记忆”会逐渐衰减。我做过一个粗略的测试:在一个对话进行到二十轮之后,再问AI“我们最开始说的数据库表名是什么”,它给出的答案经常是错的。

这个问题的根源在于,AI编程工具的上下文窗口是有限的。虽然现在很多工具都宣称支持超长上下文,但在实际使用中,过长的上下文会导致两个问题:一是响应速度变慢,二是AI对早期信息的注意力被稀释。所以我的建议是,一个对话只做一件事。比如“实现用户登录接口”是一个对话,“实现用户注册接口”就另开一个对话。这样每个对话的上下文都是干净且聚焦的,AI的输出质量也更稳定。

如果确实需要在多个任务之间共享一些背景信息,比如项目技术栈和代码规范,那就把这些信息做成一个“系统提示词”或者“项目级配置”,而不是每次都在对话里重复。很多AI编程工具都支持项目级配置,把技术栈、代码规范、常用工具类写进去,这样每个新对话都能自动带上这些背景信息。

4.2 代码片段的选取策略

给AI提供代码上下文的时候,选取哪些片段也是有讲究的。我见过有人直接把整个文件粘贴进去,几千行代码,AI看完之后反而抓不住重点。正确的做法是只提供跟当前任务直接相关的代码片段。

比如你要让AI实现一个Service层的方法,那需要提供的上下文包括:对应的Mapper接口(让AI知道有哪些数据库操作方法可用)、相关的实体类(让AI知道字段名和类型)、以及同类型的其他Service方法(让AI知道代码风格)。至于Controller层、配置类、工具类这些,除非跟当前任务直接相关,否则不需要提供。

还有一个技巧是,用注释的方式在代码片段中标注关键信息。比如在Mapper接口上方加一行注释“// 以下方法均已实现,直接调用即可”,这样AI就不会重复生成这些方法的实现。或者在实体类字段旁边标注“// 该字段为加密存储”,提醒AI在处理时注意解密。

4.3 对话中断后的恢复策略

实际工作中,一个任务经常不是一次对话就能完成的。可能聊到一半要去开会,或者被其他事情打断,回来之后发现对话已经“凉”了,AI好像忘了之前聊过什么。这时候如果直接继续问,AI的回答往往跟之前的上下文脱节。

我的做法是,在中断之前,让AI先做一个总结。比如:“请把刚才讨论的方案要点总结一下,包括接口定义、参数说明、异常处理策略。”然后把这个总结保存下来。下次继续的时候,先把总结粘贴进去,再接着聊。这样AI就能快速恢复到之前的状态。

如果中断时间比较长,或者对话已经变得很混乱,那就干脆重开一个对话。把之前的关键结论整理成一段简短的背景说明,作为新对话的开头。虽然看起来多花了几分钟,但比在一个混乱的对话里继续挣扎要高效得多。

5. 工具选型:别被“付费”两个字晃了眼

5.1 付费AI编程软件到底值不值

现在市面上的AI编程软件,免费的不少,付费的也很多。我自己的体验是,付费工具在代码补全的准确率、上下文理解能力、响应速度上确实有优势,但这个优势是否值得付费,取决于你的使用频率和场景。

如果你只是偶尔写几行脚本,或者主要用AI来查一些API用法,那免费工具完全够用。但如果你是每天写代码超过四小时的开发者,付费工具带来的效率提升是实打实的。我算过一笔账:付费工具每个月大概几十块钱,但它能帮我每天省下至少半小时的查文档、写样板代码、调试低级错误的时间。按小时成本折算,这个投入产出比是很划算的。

不过选付费工具的时候要注意一点:不要只看它宣传的“支持多少种语言”“上下文窗口多大”,这些参数在实际使用中的感知并不强。真正影响体验的是它对你常用技术栈的支持程度。比如你主要写Java,那就重点看它在Java生态里的表现,包括对Spring框架的理解、对常用工具库的熟悉程度、对Maven/Gradle项目的支持等。有些工具在Python场景下表现很好,但Java场景下就一般,这跟它训练数据的侧重有关。

5.2 不同场景下的工具组合策略

我的实际做法是,不依赖单一工具,而是根据场景组合使用。比如:

  • 日常代码补全:用IDE内置的AI插件,响应快,跟编辑器集成好,适合写一些常规的业务代码。
  • 复杂逻辑设计:用对话式的AI工具,可以来回讨论方案,适合做模块设计、算法选型、架构评审。
  • 代码审查和重构:用支持代码分析的AI工具,它能识别出一些静态分析工具发现不了的问题,比如逻辑漏洞、性能隐患。
  • 文档和注释生成:用轻量级的AI工具就行,不需要太强的推理能力,关键是快。

这种组合策略的好处是,每个场景都用最合适的工具,整体效率最高。缺点是需要在多个工具之间切换,有一定的学习成本。但用熟了之后,切换成本几乎可以忽略。

5.3 免费工具的“隐藏成本”

免费AI编程工具最大的问题不是功能少,而是“隐藏成本”高。比如响应速度慢,你等它生成代码的时候,思路可能就断了。比如上下文窗口小,你需要频繁地重新提供背景信息。比如输出质量不稳定,你需要花更多时间在review和修改上。

这些成本在单次使用的时候感知不明显,但日积月累下来,浪费的时间是惊人的。我做过一个对比:用免费工具完成一个中等复杂度的CRUD模块,从写提示词到代码可用,大概需要四十分钟。用付费工具,同样的任务,大概二十五分钟。这十五分钟的差距,主要就来自免费工具在上下文理解、代码准确率、响应速度上的不足。

所以我的建议是,如果你把AI编程当作日常工作的核心工具,那付费是值得的。但如果只是偶尔用用,免费工具完全够。关键是不要因为“免费”两个字,就忽略了时间成本。

6. 那些让我印象深刻的翻车现场

6.1 一个“简单”的分页查询引发的血案

有一次我需要实现一个用户列表的分页查询,需求很简单:支持按用户名模糊搜索,按创建时间倒序排列,每页二十条。我把需求描述给AI,它很快生成了一段代码。我扫了一眼,逻辑没问题,参数校验也有,就直接用了。

结果上线之后,有用户反馈搜索“张”的时候,结果里出现了“李”姓用户。排查了半天才发现,AI生成的SQL里,模糊搜索用的是like '%张%',但参数拼接的时候出了点问题,导致实际执行的SQL变成了like '%%',也就是匹配所有记录。更坑的是,这个bug在测试环境没被发现,因为测试数据里恰好没有“李”姓用户。

这个问题的根源在于,AI生成的代码在参数处理上有一个隐蔽的逻辑错误:当搜索关键词为空字符串时,它没有跳过模糊搜索条件,而是生成了一个匹配所有记录的like语句。这个边界条件在需求描述里没有明确说,AI也没有主动处理。

从那以后,我在提示词里都会加一句:“所有可选参数在为空时,对应的查询条件必须跳过,不能生成匹配所有记录的语句。”就这么一句话,避免了后面无数次类似的坑。

6.2 并发场景下的“幽灵bug”

另一个让我记忆深刻的坑是并发问题。当时我在做一个积分兑换的功能,用户用积分兑换商品,需要先扣减积分,再生成兑换记录。AI生成的代码逻辑很清晰:查询用户积分、判断是否足够、扣减积分、插入兑换记录。单线程测试完全没问题。

但上线之后,出现了积分扣成负数的情况。排查发现,两个请求同时进来,都查到了相同的积分余额,都判断为“足够”,然后都执行了扣减。这就是典型的并发安全问题。AI生成的代码完全没有考虑并发场景,没有加锁,也没有用乐观锁或悲观锁。

解决办法是在扣减积分的时候加一个条件:update user set points = points - ? where id = ? and points >= ?。这样即使两个请求同时进来,数据库层面也能保证不会扣成负数。这个方案比加分布式锁更轻量,也更可靠。

这件事给我的教训是,AI生成的代码默认是单线程思维,任何涉及共享资源修改的场景,都必须自己加上并发控制。后来我在代码审查清单里专门加了一条:“涉及余额、库存、计数等共享资源修改的操作,是否考虑了并发安全?”

6.3 被AI“优化”掉的业务逻辑

还有一次更隐蔽的坑。我在让AI重构一段老代码的时候,它“贴心”地帮我简化了一些它认为“冗余”的逻辑。比如原来有一段代码,在用户注册成功之后,会先发一封欢迎邮件,再发一条站内信,最后更新用户状态。AI觉得这三个操作可以并行执行,就改成了异步调用。

结果问题来了:欢迎邮件里包含了一个激活链接,这个链接的生成依赖于用户状态是“待激活”。但AI改成异步之后,更新用户状态的操作可能先执行,导致邮件里的激活链接生成失败。这个bug在测试环境没复现,因为测试环境的异步任务执行顺序恰好是对的。到了生产环境,并发量一上来,顺序就乱了。

这个坑的教训是,AI在重构代码的时候,往往会从“代码简洁性”的角度出发,而忽略了业务逻辑中的时序依赖。所以在让AI重构代码之前,一定要把业务规则和时序要求说清楚。我现在的做法是,在提示词里明确写:“以下操作有严格的执行顺序要求,请勿改为并行或异步:1. xxx 2. xxx 3. xxx。”

7. 把AI编程真正融入工作流的一些心得

7.1 建立自己的“AI代码规范”

用AI编程时间长了之后,我慢慢形成了一套自己的“AI代码规范”。这套规范不是约束AI的,而是约束我自己的——规定在什么场景下用AI、怎么用、用到什么程度。

比如:核心业务逻辑的代码,AI只用来生成骨架,具体实现自己写;工具类、DTO、配置文件这些,AI生成之后简单review就能用;单元测试的边界用例,AI生成之后必须自己补充;涉及资金、权限、隐私的代码,AI生成的版本只能作为参考,不能直接使用。

这套规范的好处是,它让我对AI的依赖变得“可控”。我知道哪些地方可以放心交给AI,哪些地方必须自己把关。这样既享受了AI带来的效率提升,又不会因为过度依赖而引入风险。

7.2 团队协作中的AI编程策略

如果你在团队里推广AI编程,有几个点需要特别注意。首先是代码风格的一致性。每个人用AI的方式不一样,生成的代码风格也可能不同。解决办法是制定统一的提示词模板和代码规范,让大家的AI输出保持在同一水平线上。

其次是代码审查的标准。AI生成的代码和手写代码在审查时应该用同一套标准,不能因为“这是AI写的”就放松要求。相反,AI代码更需要仔细审查,因为它的问题往往更隐蔽。

最后是知识共享。团队里谁发现了AI编程的好技巧、好工具、好提示词,应该及时分享出来。我们团队内部有一个共享文档,专门记录AI编程的踩坑经验和解决方案。这个文档更新得越频繁,整个团队的效率提升就越明显。

7.3 保持对代码的“手感”

最后一个心得可能有点反直觉:用AI编程越多,越要保持自己手写代码的习惯。我见过一些开发者,用AI用久了之后,离开AI就写不出完整的代码了。这其实是一种能力的退化。

我的做法是,每周至少留出半天时间,完全不借助AI,纯手写代码。可以是一个小工具,也可以是一个算法题,目的不是产出什么有价值的东西,而是保持对代码的“手感”。这种手感包括:对语法细节的敏感度、对代码结构的直觉、对性能问题的判断力。这些东西一旦退化,再想捡回来就难了。

而且手写代码的时候,往往能发现一些AI编程时忽略的问题。因为AI帮你把细节都填好了,你容易跳过思考的过程。手写的时候,每一个变量名、每一个条件判断都需要自己决定,这个过程本身就是一种深度思考。

提示:如果你发现自己最近用AI编程时越来越“无脑”——AI给什么就用什么,不怎么思考了——那说明你需要停下来,手写几天代码找回状态。这不是矫情,是实实在在的经验。

8. 关于AI编程的未来,我的个人判断

AI编程工具的发展速度确实很快,每隔几个月就有新功能、新工具出来。但我觉得,不管工具怎么变,有些东西是不变的:对业务的理解、对代码质量的追求、对边界条件的敏感、对并发安全的重视。这些能力不会因为AI的出现而贬值,反而会因为AI的普及而变得更加重要。

因为当AI能帮你写出“看起来没问题”的代码时,真正区分优秀开发者和普通开发者的,就是发现那些“看起来没问题”的代码里隐藏问题的能力。这种能力需要扎实的基础、丰富的经验和严谨的态度,AI暂时还替代不了。

所以我的态度是:拥抱AI编程,但不依赖AI编程。把它当作一个强大的助手,而不是一个可以托付一切的伙伴。该自己把关的地方,一步都不能省。该自己思考的问题,一个都不能少。这样用AI,才是真正的效率提升,而不是把风险往后推。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进