這不是標題黨。上個月,我用 Claude Code + Cursor 花了 7 天從零寫完了一個內部工具平台,提前兩週交付,老闆在週會上點名表揚。然後上線第一天,告警群炸了 47 條訊息。
我們團隊接了一個內部需求:給營運團隊做一個資料儀表板 + 工單系統。
需求不複雜,技術棧是 React + Node.js + PostgreSQL,正常排程兩個人三週。但當時另一個同事被抽去救火了,變成我一個人,排程不變。
我想,這不正好?上個月剛充了 Claude Code Max,Cursor 也續了費,這種 CRUD 專案,AI 寫程式不是手到擒來?
於是我做了一個大膽的決定:全程用 AI 輔助開發,目標一週交付。
結果是——我確實一週寫完了。程式碼量大概 1.2 萬行,前後端 + 資料庫 + 部署腳本,全套。
但上線後第一天,就出了三個線上事故。
這篇文章,我會把這 7 天的真實經歷和踩的 5 個坑完整地寫出來。不是為了黑 AI,AI 確實讓我效率提升了 3 倍以上。但那些「AI 寫程式真香」的文章不會告訴你的暗面,我來說。
第三天,我讓 Claude Code 幫我寫了一個資料聚合介面,把工單按部門、狀態、時間維度做分組統計。
AI 給的程式碼很漂亮:
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。
修復後的程式碼:
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 不懂你的業務時區,不懂你的使用者是誰,不懂你的資料量級。這些上下文,它不會主動問你,你不說它就不管。
第二天,我讓 AI 幫我寫一個檔案上傳模組,支援分片上傳、斷點續傳、進度顯示。
AI 非常興奮地給我寫了將近 400 行程式碼,包括:
寫得非常完整,我當時還覺得「好厲害」。
直到上線後,有個同事看了我的程式碼,來了一句:
「這個……你為什麼不用 TUS 協定?
tus-js-client+tus-node-server兩個套件加起來不到 20 行設定就搞定了。」
我當時臉就綠了。
AI 預設行為是從零實作,而不是先搜尋有沒有成熟方案。它不會說「這個需求其實用 XXX 函式庫三行程式碼就能搞定」,因為它的訓練目標就是生成程式碼,而不是幫你少寫程式碼。
教訓:在讓 AI 寫程式之前,先問它一個問題——「這個需求有沒有成熟的開源方案?列出前 3 個最流行的,比較優缺點。」
這一個 prompt 能幫你省掉 80% 的輪子程式碼。
這個坑差點讓我被開除。
第五天,我在配置部署腳本的時候,讓 AI 幫我寫 Docker Compose 檔案。AI 很「貼心」地幫我生成了一個完整的 .env.example:
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 追蹤了。
教訓:
git diff --cached 檢查暫存區.gitignore 和 git-secrets 掃描# 安裝 git-secrets,自動阻止密鑰提交
git secrets --install
git secrets --register-aws
git secrets --add 'password\s*=\s*.+'
第六天,我想著上線前要補測試。讓 AI 幫我生成了全套單元測試。
跑完一看,覆蓋率 92%。漂亮!
但仔細一看測試內容,我人麻了:
// 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 個場景,然後為每個場景寫測試。」
這是最隱蔽的坑。
因為 AI 寫程式太快了,我在第一天就直接開始寫程式。沒有畫架構圖,沒有定義 API 契約,沒有設計資料庫 ER 圖。
AI 說啥我就用啥。前端元件的 props 定義,後端的 API 路由結構,資料庫的表設計——全部是「邊寫邊定」。
結果上線一週後,營運提了三個新需求:
我一看現有的資料庫設計,工單表裡 assignee 就是一個 VARCHAR 欄位存的人名字串。要支援轉交,意味著要有轉交紀錄、轉交時間、歷史負責人鏈路。整個表結構要重新設計。
審批流程更誇張——現有的狀態欄位就是一個 ENUM('open', 'in_progress', 'closed'),要做審批流程相當於要引入狀態機,加審批人、審批意見、退回重提等邏輯。
如果當初花半天時間做設計,這些擴充性都能提前預留。但 AI 讓我產生了「反正寫程式很快」的幻覺,跳過了最重要的設計環節。
教訓:AI 加速的是「寫程式」這個環節,但軟體開發中最貴的從來不是寫程式——是設計。在動手之前,先讓 AI 幫你做設計評審:
prompt: "我要開發一個工單系統,核心功能是 XXX。
請幫我做以下設計:
1. 資料庫 ER 圖(考慮未來可能的擴充:轉交、審批流程、標籤系統)
2. API 介面契約(RESTful,列出所有端點)
3. 前端頁面路由結構
4. 你認為這個系統最容易在哪些地方埋下技術債?"
上線後那個週末,我花了兩天修 bug + 重構。回頭看這 7 天,我的結論是:
| 能力 | AI 能做 | AI 不能做 |
|---|---|---|
| 寫程式 | 快速生成 | 理解業務上下文 |
| 測試 | 生成模板 | 識別真正的邊界場景 |
| 架構設計 | 給出方案 | 判斷哪個方案適合你的團隊 |
| 安全 | 生成示例設定 | 保證你不犯低級錯誤 |
| Debug | 分析錯誤日誌 | 理解線上環境的複雜性 |
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 程式設計的實戰經驗。