这不是标题党。上个月,我用 Claude Code + Cursor 花了 7 天从零写完了一个内部工具平台,提前两周交付,老板在周会上点名表扬。然后上线第一天,报警群炸了 47 条消息。
先说背景
我们团队接了一个内部需求:给运营团队做一个数据看板 + 工单系统。
需求不复杂,技术栈是 React + Node.js + PostgreSQL,正常排期两个人三周。但当时另一个同事被抽去救火了,变成我一个人,排期不变。
我想,这不正好?上个月刚充了 Claude Code Max,Cursor 也续了费,这种 CRUD 项目,AI 写代码不是手到擒来?
于是我做了一个大胆的决定:全程用 AI 辅助开发,目标一周交付。
结果是------我确实一周写完了。代码量大概 1.2 万行,前后端 + 数据库 + 部署脚本,全套。
但上线后第一天,就出了三个线上事故。
这篇文章,我会把这 7 天的真实经历和踩的 5 个坑完整地写出来。不是为了黑 AI,AI 确实让我效率提升了 3 倍以上。但那些"AI 写代码真香"的文章不会告诉你的暗面,我来说。
坑 1:AI 生成的代码"看起来对",但数据边界全是雷
第三天,我让 Claude Code 帮我写了一个数据聚合接口,把工单按部门、状态、时间维度做分组统计。
AI 给的代码很漂亮:
javascript
async function getTicketStats(startDate, endDate, department) {
const result = await db.query(`
SELECT
department,
status,
DATE_TRUNC('day', created_at) as date,
COUNT(*) as count
FROM tickets
WHERE created_at BETWEEN $1 AND $2
AND ($3 IS NULL OR department = $3)
GROUP BY department, status, DATE_TRUNC('day', created_at)
ORDER BY date DESC
`, [startDate, endDate, department]);
return result.rows;
}
一眼看去,没问题。测试环境跑了一下,数据出来了,图表也渲染了。
上线后的表现:运营总监说"这个数据不对"。
问题出在哪?
- 时区问题 :
DATE_TRUNC默认用的是 UTC,但运营看的是北京时间。跨天的工单被归到了前一天。 - 空值处理 :当
department传undefined时,$3 IS NULL这个条件在 PostgreSQL 中的行为不是"忽略过滤",而是匹配department IS NULL的记录。 - 大分页缺失:没有 LIMIT,当时间范围选了一整年,直接返回了 12 万行数据,前端图表库 echarts 卡死了。
这三个问题,每一个在代码层面都是"AI 写得没错",但在业务层面全是 bug。
修复后的代码:
javascript
async function getTicketStats(startDate, endDate, department) {
const conditions = [
'created_at >= $1',
'created_at < $2'
];
const params = [startDate, endDate];
if (department) {
conditions.push(`department = $${params.length + 1}`);
params.push(department);
}
const result = await db.query(`
SELECT
department,
status,
DATE_TRUNC('day', created_at AT TIME ZONE 'Asia/Shanghai') as date,
COUNT(*) as count
FROM tickets
WHERE ${conditions.join(' AND ')}
GROUP BY department, status, date
ORDER BY date DESC
LIMIT 10000
`, params);
return result.rows;
}
教训:AI 不懂你的业务时区,不懂你的用户是谁,不懂你的数据量级。这些上下文,它不会主动问你,你不说它就不管。
坑 2:AI 特别擅长"造轮子",但从不告诉你有现成的
第二天,我让 AI 帮我写一个文件上传模块,支持分片上传、断点续传、进度显示。
AI 非常兴奋地给我写了将近 400 行代码,包括:
- 前端的分片逻辑
- MD5 校验
- 后端的分片合并
- 进度回调
写得非常完整,我当时还觉得"好厉害"。
直到上线后,有个同事看了我的代码,来了一句:
"这个......你为什么不用 TUS 协议?
tus-js-client+tus-node-server两个包加起来不到 20 行配置就搞定了。"
我当时脸就绿了。
AI 默认行为是从零实现 ,而不是先搜索有没有成熟方案。它不会说"这个需求其实用 XXX 库三行代码就能搞定",因为它的训练目标就是生成代码,而不是帮你少写代码。
教训:在让 AI 写代码之前,先问它一个问题------"这个需求有没有成熟的开源方案?列出前 3 个最流行的,对比优缺点。"
这一个 prompt 能帮你省掉 80% 的轮子代码。
坑 3:复制粘贴了 AI 生成的环境变量,差点把数据库密码提交到 Git
这个坑差点让我被开除。
第五天,我在配置部署脚本的时候,让 AI 帮我写 Docker Compose 文件。AI 很"贴心"地帮我生成了一个完整的 .env.example:
env
DATABASE_URL=postgresql://admin:your_password_here@localhost:5432/dashboard
JWT_SECRET=your-super-secret-key-change-this
REDIS_URL=redis://localhost:6379
我当时赶进度,直接把 your_password_here 改成了真实密码,然后继续写代码。
那天晚上提交代码的时候,我习惯性 git add .,幸好提交前我多看了一眼 diff------.env 文件赫然在列。
如果这个文件被推到了远端,里面有生产数据库的真实密码。
后来我检查了一下,发现 AI 生成的项目里,.gitignore 里确实有 .env,但它同时生成了 .env 文件本身。问题是,在某些情况下我手动删过 .gitignore 的缓存(因为之前另一个文件不生效我排查过),导致 .env 被 Git 追踪了。
教训:
- 永远不要在 AI 生成的配置文件模板里直接填写真实密钥
- 提交前必须
git diff --cached检查暂存区 - 在项目初始化时,第一件事就是配好
.gitignore和git-secrets扫描
bash
# 安装 git-secrets,自动阻止密钥提交
git secrets --install
git secrets --register-aws
git secrets --add 'password\s*=\s*.+'
坑 4:AI 写的单元测试,覆盖率 90%,但全是"自嗨型"测试
第六天,我想着上线前要补测试。让 AI 帮我生成了全套单元测试。
跑完一看,覆盖率 92%。漂亮!
但仔细一看测试内容,我人麻了:
javascript
// AI 生成的测试
describe('createTicket', () => {
it('should create a ticket successfully', async () => {
const mockTicket = {
title: 'Test Ticket',
description: 'Test Description',
department: 'Engineering',
priority: 'high'
};
db.query.mockResolvedValue({ rows: [{ id: 1, ...mockTicket }] });
const result = await createTicket(mockTicket);
expect(result).toEqual({ id: 1, ...mockTicket });
expect(db.query).toHaveBeenCalledTimes(1);
});
});
看出问题了吗?
它在测试自己 Mock 的返回值。 db.query 被 mock 成返回 { id: 1, ...mockTicket },然后断言结果等于 { id: 1, ...mockTicket }。
这个测试永远不会失败,因为它测的不是业务逻辑,而是"mock 框架能不能正常返回我设定的值"。
真正应该测的是什么?
- 当
title为空时,是否抛出参数校验错误? - 当
priority传了一个非法值(比如"urgent"),行为是什么? - 当数据库连接失败时,错误是否被正确包装和上报?
- 当并发创建同标题工单时,是否有幂等处理?
这些边界场景,AI 一个都没测。
教训:AI 生成的测试覆盖率是虚假繁荣。让 AI 写测试时,要明确要求:"不要测试正常流程,只测试边界条件和异常场景。列出你认为最可能出 bug 的 5 个场景,然后为每个场景写测试。"
坑 5:速度太快 = 跳过了设计,技术债一周后就爆了
这是最隐蔽的坑。
因为 AI 写代码太快了,我在第一天就直接开始写代码。没有画架构图,没有定义 API 契约,没有设计数据库 ER 图。
AI 说啥我就用啥。前端组件的 props 定义,后端的 API 路由结构,数据库的表设计------全部是"边写边定"。
结果上线一周后,运营提了三个新需求:
- 工单要支持"转交"功能
- 数据看板要加"同比/环比"对比
- 要加"审批流"
我一看现有的数据库设计,工单表里 assignee 就是一个 VARCHAR 字段存的人名字符串。要支持转交,意味着要有转交记录、转交时间、历史负责人链路。整个表结构要重新设计。
审批流更夸张------现有的状态字段就是一个 ENUM('open', 'in_progress', 'closed'),要做审批流相当于要引入状态机,加审批人、审批意见、驳回重提等逻辑。
如果当初花半天时间做设计,这些扩展性都能提前预留。但 AI 让我产生了"反正写代码很快"的幻觉,跳过了最重要的设计环节。
教训:AI 加速的是"写代码"这个环节,但软件开发中最贵的从来不是写代码------是设计。在动手之前,先让 AI 帮你做设计评审:
markdown
prompt: "我要开发一个工单系统,核心功能是 XXX。
请帮我做以下设计:
1. 数据库 ER 图(考虑未来可能的扩展:转交、审批流、标签系统)
2. API 接口契约(RESTful,列出所有端点)
3. 前端页面路由结构
4. 你认为这个系统最容易在哪些地方埋下技术债?"
复盘:一周后的反思
上线后那个周末,我花了两天修 bug + 重构。回头看这 7 天,我的结论是:
AI 确实帮了我
- 1.2 万行代码,如果纯手写至少要三周,AI 帮我压缩到了一周
- 很多样板代码(CRUD 接口、表单校验、SQL 建表语句)AI 写得又快又标准
- 遇到不熟的库(比如 echarts 的配置项),AI 比翻文档快 10 倍
但 AI 不能替代的
| 能力 | AI 能做 | AI 不能做 |
|---|---|---|
| 写代码 | 快速生成 | 理解业务上下文 |
| 测试 | 生成模板 | 识别真正的边界场景 |
| 架构设计 | 给出方案 | 判断哪个方案适合你的团队 |
| 安全 | 生成示例配置 | 保证你不犯低级错误 |
| Debug | 分析错误日志 | 理解线上环境的复杂性 |
我现在的 AI 编程工作流(踩完坑后的版本)
markdown
1. 需求分析(自己做)→ 30 分钟
2. 让 AI 做架构设计 + 自己评审 → 2 小时
3. 定义 API 契约和数据模型(和 AI 对话式完成)→ 1 小时
4. AI 生成代码 + 自己逐文件 Review → 主要开发时间
5. 自己写核心业务逻辑的测试,AI 补充工具函数的测试
6. 上线前安全检查清单(手动 + git-secrets)
关键原则:AI 是副驾驶,不是自动驾驶。你可以让它帮你打方向盘,但你不能闭着眼睛。
写在最后
我不是要劝你别用 AI 写代码。恰恰相反,经过这次踩坑,我现在更离不开 AI 了。
但我想说的是:AI 让写代码变快了,但没有让写"好代码"变容易。 恰恰相反,因为生成速度太快,很多本该深思熟虑的环节被跳过了。
下次当你用 AI 一天写完了别人一周的量,先别急着发朋友圈炫耀。
问自己一句:这些代码,你敢不看直接上线吗?
如果答案是"不敢"------那 AI 帮你省下来的时间,就应该花在 Review 上。
如果你也在用 AI 写代码,欢迎评论区说说你踩过的坑。点赞收藏不迷路,我会持续分享 AI 编程的实战经验。