AI编程时代的一些心得
前几天,团队里一个小伙伴兴奋地跟我说:"Claude Code 太神了!我直接把整个需求甩给它,一个小时就搞定了三天的活儿!"
我问他:那代码质量怎么样?
他支支吾吾:好像有些问题,但大部分能跑...
这就是典型的"AI 蜜月期陷阱"。很多人以为有了 AI 编程工具,就可以"甩手掌柜"。但现实是残酷的。
如果你把每次 AI 生成代码的准确率设为 90%,那么连续 10 次操作后,整体准确率就会下降到 0.9^10 ≈ 34%。
一句话总结:AI 编程不是替代,而是协作。你要做的不是被 AI 超越,而是和 AI 一起进化。
# 一、底层逻辑:重新定义人机分工
很多人搞错了 AI 编程工具的定位。
错误认知:AI 是万能的代码生成器,我只需要提需求。
正确认知:AI 是高效的编程助手,我负责决策和质量把控。
# 人与AI的边界在哪里?
人的职责(决策层):
- 深入理解产品需求,进行任务分解
- 架构设计和技术选型
- 代码审查和质量把控
- 异常处理和风险管控
AI的职责(执行层):
- 根据明确需求生成代码
- 重构和格式化代码
- 编写测试用例
- 文档生成和注释补充
这就是"道、术、器"的分工:
- 道:产品方向,你说了算
- 术:实现方法,你来把关
- 器:具体编码,AI 来加速
# 二、核心挑战:AI的不确定性问题
在深入策略之前,必须先认清AI编程的一个本质问题:生成结果的不确定性。
# 同一个prompt,不同的输出
你可能遇到过这样的情况:
- 昨天让AI生成的代码跑得好好的
- 今天用同样的提示词,生成的代码却有bug
- 同一个需求问三次,得到三种不同的实现方案
这不是AI"抽风",而是大语言模型的天然特性。
原因分析:
- LLM使用概率采样生成内容,每次输出都有随机性
- 上下文的细微差异会导致不同的推理路径
temperature参数控制着输出的随机程度
应对策略:
- 建立基线版本:第一次生成满意的代码后,立即保存为参考版本
- 使用版本控制:每次AI生成的代码都要
commit,方便回溯 - 固化成功经验:把好的
prompt和生成结果整理成文档,后续直接复用 - 设置温度参数:如果工具支持,降低
temperature参数以获得更确定的输出
这就引出了业界的两个重要解决方案:Skill方案和Spec方案。
# 三、业界方案:Skill与Spec
# Skill方案:经验的模块化复用
什么是Skill?
Skill是可复用的AI指令包,包含了特定任务的最佳实践和执行步骤。
解决什么问题?
- 重复劳动:不用每次都从零开始写
prompt - 经验流失:团队成员的成功经验可以固化成
skill - 质量波动:标准化的指令确保输出一致性
典型应用场景:
- 代码审查:
@code-reviewskill自动检查代码规范、安全漏洞 - 测试生成:
@test-genskill按照团队的测试框架生成单元测试 - 文档生成:
@api-docskill根据代码自动生成API文档
实践建议:
## 创建自己的Skill库
1. 识别高频重复任务
2. 提炼成功的prompt模板
3. 编写skill.md文件
4. 在团队内共享和迭代
# Spec方案:需求的精确表达
什么是Spec?
Spec(规格说明)是需求的形式化、可执行的表达,作为"唯一真相来源"指导AI生成代码。
解决什么问题?
- 意图走样:需求到代码的理解偏差
- 文档废弃:代码和文档不一致
- 过度设计:AI自由发挥添加不必要的功能
工作流程:
- 定义规格(
/specify):明确WHAT和WHY,禁止HOW - 澄清风险(
/clarify):暴露潜在问题和边界条件 - 技术方案(
/plan):确定实现路径 - 任务拆解(
/tasks):分解成可并行的子任务 - 一致性校验(
/analyze):确保代码符合规格 - 落地实现(
/implement):强制TDD,让规格成为测试用例
实践案例:
## 错误方式
"帮我做个用户登录功能"
→ AI自由发挥:加了第三方登录、记住密码、验证码...
## 正确方式(Spec驱动)
**WHAT**:用户使用手机号+密码登录
**WHY**:满足MVP快速上线需求
**约束**:
- 只支持手机号+密码,不支持第三方登录
- 密码使用bcrypt加密
- 登录失败3次锁定账户10分钟
# Skill vs Spec:如何选择?
| 维度 | Skill方案 | Spec方案 |
|---|---|---|
| 适用场景 | 标准化、重复性任务 | 复杂业务需求开发 |
| 核心价值 | 经验复用、提高效率 | 需求精确、质量保障 |
| 学习成本 | 低,即学即用 | 中等,需要理解工作流 |
| 团队协作 | 共享skill库 | 共享spec文档 |
| 典型工具 | Claude Code、Qoder | Cursor、Claude Code |
组合使用:
- 用
Spec定义复杂需求 - 用
Skill执行标准任务(如代码审查、测试生成) - 两者结合,既保证需求准确,又提高执行效率
# 四、五个关键策略
# 1. 任务分解:化整为零,步步为营
反面案例: "帮我写一个电商系统,包含用户管理、商品管理、订单系统、支付接口..."
这样的需求,AI 生成的代码质量约等于"能跑的 Demo"。
正面案例:
"请帮我实现用户注册接口,包含手机号验证、密码加密、重复注册校验,返回 JSON 格式的成功或错误信息。"
每个任务控制在 20-30 分钟内完成,可以独立测试,可以快速回滚。
# 2. 及时修正:90% 准确率的复利陷阱
假设你连续让 AI 生成 10 次代码,每次准确率 90%:
- 第1次:90%
- 第2次:90% × 90% = 81%
- 第3次:90%³ = 72.9%
- ...
- 第10次:90%¹⁰ = 34.9%
解决方案:每次生成后立即验证,确保产品逻辑 100% 正确。小 bug 可以容忍,业务逻辑绝不能错。
# 3. 选择熟悉的技术栈:降低沟通成本
原则:用你最熟悉的编程语言和框架。
为什么?
- 你能快速识别 AI 生成代码的问题
- 你知道这门语言的最佳实践
- 你能给出更精准的
prompt
不要因为 AI 支持某个新技术就盲目尝试。稳定产出比炫技更重要。
# 4. 严苛的质量管理
代码规范检查:
- 统一的代码格式化工具(
Prettier、ESLint、Black等) - 命名规范要求
- 注释完整性检查
测试要求:
- 核心业务逻辑必须有单元测试
- 关键接口必须有集成测试
- 边界条件和异常情况覆盖
最佳实践遵循:
- 设计模式的正确应用
- 安全编码标准
- 性能优化要求
# 5. 奥卡姆剃刀原则:去繁为简
AI 有个特点:它总是倾向于生成"看起来很完整"的代码,包含很多你可能用不到的功能。
三个删减原则:
- 能不引入的依赖就不引入
- 能推迟的功能就推迟
- 能简化的逻辑就简化
复杂度是技术债务,越早引入,利息越高。
# 五、实战经验:一个完整的开发案例
上周接到一个需求:给现有系统加一个"用户积分兑换"功能。我用AI辅助开发,从开始到测试通过,总共花了2小时。
让我带你走一遍完整流程,看看这2小时是怎么分配的。
# 第一步:需求拆解(15分钟)
我没有直接让AI写代码,而是先在纸上画了个思维导图:
积分兑换功能
├── 积分查询接口(10分钟)
├── 兑换商品列表(15分钟)
├── 兑换下单逻辑(30分钟)
│ ├── 积分扣减
│ ├── 订单生成
│ └── 库存扣减
└── 兑换记录查询(15分钟)
关键决策:我把"兑换下单"拆成了3个原子操作,每个都能独立测试。这样即使中间出错,也能快速定位。
然后我创建了一个spec.md文件:
## 积分兑换功能规格
**目标**:用户用积分兑换实物商品
**约束**:
- 积分不足不能兑换
- 库存为0不能兑换
- 兑换成功后积分立即扣减,不可撤销
- 兑换失败全部回滚
**不做**:
- 不支持部分积分+现金
- 不支持积分退回
这个spec.md后面会反复用到,每次让AI生成代码时,我都把它贴进prompt里。
# 第二步:逐个击破(90分钟)
第一个任务:积分查询接口(实际用时12分钟)
我的prompt:
根据以下规格,实现用户积分查询接口:
[粘贴spec.md内容]
技术栈:Spring Boot + MyBatis
要求:
1. RESTful风格,GET /api/points/{userId}
2. 返回JSON:{userId, points, level}
3. 用户不存在返回404
AI生成代码后,我做了3件事:
- 立即运行:启动项目,用Postman测试
- 边界测试:试了不存在的userId、负数userId
- 代码审查:检查了
SQL注入风险(AI用的是参数化查询,没问题)
踩坑:AI给的返回值用了int类型,我改成了Long,因为积分可能很大。
第二个任务:兑换下单逻辑(实际用时45分钟)
这是最复杂的部分,我继续拆:
我: 先实现积分扣减,只做这一件事
AI: [生成PointService.deductPoints方法]
我: 测试通过。现在加订单生成,要保证原子性
AI: [生成OrderService,加了@Transactional]
我: 等等,如果库存扣减失败怎么办?
AI: [重新生成,加了分布式锁]
我: 不需要分布式锁,我们还没上分布式。用数据库悲观锁
AI: [修正为SELECT ... FOR UPDATE]
注意这个过程:我没有一次性要求所有功能,而是一层一层加,每加一层都测试。
第3次生成时AI"自作主张"加了分布式锁,我立即叫停。这就是为什么要熟悉技术栈——你得知道AI什么时候在过度设计。
# 第三步:集成测试(15分钟)
所有接口都开发完,我写了个测试脚本:
# 1. 查询积分:应该返回1000
curl GET /api/points/123
# 2. 兑换商品(需要500积分):应该成功
curl POST /api/exchange -d '{"userId":123, "productId":456}'
# 3. 再次查询:应该返回500
curl GET /api/points/123
# 4. 再兑换一次:应该失败(积分不足)
curl POST /api/exchange -d '{"userId":123, "productId":789}'
全部通过!但我还没急着提交。
# 第四步:回顾和清理(20分钟)
我做了几件"可选但很重要"的事:
- 删除冗余代码:AI生成了一些没用到的工具方法,全删掉
- 统一异常处理:AI在3个地方用了不同的异常类,我统一成自定义的
ExchangeException - 补充注释:给关键的事务边界加了注释
- 运行
ESLint(后端是Checkstyle):修复了几个代码风格问题
最后,我更新了spec.md,记录了一个重要决策:
## 技术决策
- 使用数据库悲观锁保证库存一致性(理由:单机部署,无需分布式锁)
- 兑换失败不记录日志(理由:减少表污染,改从订单表查失败原因)
下次类似需求,我或者团队其他人可以直接参考。
# 复盘:为什么这么顺?
回头看,这2小时的关键点:
- 任务拆解用了15分钟,但后面省了至少30分钟调试时间
- spec.md贯穿始终,AI没有一次"理解偏差"
- 每个任务都立即测试,没有积累bug
- 主动叫停过度设计(分布式锁那次),保持了简洁
如果一开始就让AI"写个积分兑换功能",大概率要花4小时,还不一定能用。
# 六、避坑指南:我踩过的那些坑
# 坑1:被"一次搞定"的幻觉欺骗
事故现场:
三个月前,我接到一个紧急需求:"今天下班前上线一个数据导出功能"。
我想:AI这么强,直接让它写完不就行了?
于是我甩了一个"大而全"的prompt:
写一个Excel导出功能,支持:
- 用户列表导出
- 订单列表导出
- 自定义字段选择
- 导出进度显示
- 导出历史记录
- 邮件发送导出文件
AI很快生成了500多行代码,看起来"功能齐全"。
翻车过程:
- 17:00 上线,测试点了"导出"按钮
- 17:02 服务器
OutOfMemory,系统挂了 - 17:05 紧急回滚,老板脸黑得像锅底
事后分析:
AI生成的代码有3个致命问题:
- 一次性加载10万条数据到内存
- 导出进度用的是全局变量,多用户并发直接乱套
- 邮件发送没做异步,阻塞了主线程
如果我按任务拆解,逐个测试,这些问题在第一阶段就会暴露。
教训:越着急,越要拆小。你以为的"节省时间",最后都会加倍偿还。
# 坑2:盲目相信AI的"最佳实践"
事故现场:
有次让AI写一个文件上传功能,它很"贴心"地用了commons-fileupload库,还加了完善的异常处理。
我一看:哇,考虑得真周到!直接合并。
翻车过程:
上线第二天,安全部门找上门:你的上传接口有任意文件上传漏洞。
我一查代码,发现AI只检查了文件后缀,没检查文件内容:
// AI生成的代码
if (filename.endsWith(".jpg") || filename.endsWith(".png")) {
// 允许上传
}
攻击者上传一个virus.jpg(实际是可执行文件),直接绕过。
事后分析:
AI确实知道"最佳实践",但它不知道你的业务场景:
- 我们的系统要求医疗级别的安全标准
- AI给的是"通用互联网应用"的方案
教训:AI生成的代码,安全相关的部分必须人工审查。SQL注入、XSS、文件上传、权限校验,这些地方不能偷懒。
# 坑3:技术债的温水煮青蛙
事故现场:
用AI写了个管理后台,每次加新功能,我都直接让AI"在原有代码上加"。
两个月后,代码变成这样:
- 一个
Service类3000行 - 一个方法里嵌套7层
if-else - 同一个逻辑在3个地方重复
我想重构,发现已经看不懂了。
翻车过程:
需要加一个"批量导入"功能,我让AI改,它说:
当前代码结构较复杂,建议先重构再添加新功能
我:...你当初不是你写的吗?
事后分析:
每次让AI"加功能"时,我都没做这两件事:
- 删除旧代码:担心删错,结果越积越多
- 重构:总想"下次再说",结果下次变成了"永远不"
教训:每5次AI生成,就要主动重构1次。不要等到"看不懂"才想起来。
具体做法:
- 设置一个提醒:代码行数超过200,必须拆分
- 发现重复代码(3次以上),立即提取
- 每周五下午,专门做一次"代码清理"
# 坑4:忽略AI的"不确定性"
事故现场:
有个登录功能,我让AI生成了一版,测试通过,直接上线。
第二天,产品经理说:"登录有点问题,你再优化一下"。
我用**同样的prompt**让AI重新生成,结果:
- 原来的
JWT过期时间是7天,新版变成了24小时 - 原来的密码加密用的是
BCrypt,新版变成了MD5
我一合并,线上用户全部登录失效。
事后分析:
AI每次生成都有随机性,即使prompt一样,细节也可能不同。
我犯的错误:
- 没有保存第一版的"成功配置"
- 没有用
git diff对比新旧代码 - 没有在测试环境验证
教训:建立"AI生成代码"的版本管理规范:
# 每次AI生成新代码,立即创建分支
git checkout -b ai/feature-name-v1
# 提交时注明prompt
git commit -m "feat: 登录功能 | prompt: [登录功能,JWT 7天]"
# 如果要重新生成,对比差异
git diff ai/feature-name-v1 ai/feature-name-v2
# 坑5:技术栈的"冒险"
事故现场:
看到AI支持Rust,我想:正好学学新技术,让AI教我写。
结果写了一周,AI生成的代码:
- 我看不懂哪里有问题
- 报错信息我不知道怎么搜
- 调试工具我不会用
最后还是换回Java重写,白白浪费一周。
教训:AI不是你的导师,它是你的助手。
用AI学新技术可以,但要分两步:
- 先自己学基础:看完官方教程,写几个
Hello World - 再让AI辅助提升:这时你能判断AI生成的代码对不对
记住:AI放大的是你的能力,不是你的无知。
# 七、成功协作的三个层次
# 初级:工具使用者(能用)
- 会基本的
prompt编写 - 能让 AI 生成可运行的代码
- 知道简单的调试方法
# 中级:协作伙伴(用好)
- 懂得任务分解和质量把控
- 能选择合适的 AI 工具
- 有完整的开发流程
# 高级:效率放大器(用精)
- 形成个人的 AI 协作方法论
- 能培训团队成员高效协作
- 持续迭代和优化工作流程
# 结语
AI 编程工具不是来替代程序员的,而是来放大程序员能力的。
关键在于:你要始终保持产品思维和质量意识。AI 负责提升你的编码速度,你负责确保代码的质量和正确性。
这样的协作关系下,1+1 不只等于 2,而是等于 10。
祝你变得更强!