AI 編程進入下半場:從 Vibe 到 Spec
整理版優先睇
AI編程從Vibe Coding進化到Spec Coding,以規範為骨架提升工程化交付質量
呢篇文章探討AI編程由Vibe Coding走向Spec Coding嘅演變。作者指出,而家團隊最大問題並唔係唔識寫code,而係冇辦法將「點解咁寫」變成可驗證、可協作、可演進嘅規範。文章假定用Node.js 20 + TypeScript + Vitest + Claude Code,目標係將AI輔助開發提升到生產級別。
Vibe Coding喺探索期好快,但一到生產階段就會出現4個問題:意圖丟失、結果漂移、驗證滯後、經驗冇得重用。解決方法係Spec Coding,將規範當成source of truth,分三層:功能規範(What)、架構規範(How語言無關)、實現規範(How語言相關)。喺Claude Code入面,對應嘅就係CLAUDE.md、.claude/rules/、specs/*.md呢啲檔案。
文章用通知系統做示範,完整展示咗「先寫規範、再實現、最後驗證」嘅流程。佢總結咗最佳實踐,包括規範同code放埋同一個倉庫、每個規範都要有驗收標準同非功能要求、規範改動一定要觸發自動測試。常見陷阱就有淨係寫happy path、將實作細節當成需求、畀AI生成咗就算唔做驗證。最後提出一條由Vibe到Spec嘅漸進路徑:探索期用Vibe快速驗證,沉澱期抽成specs,重建期基於規範重做並強制測試。一句總結:速度來自Vibe,質量來自Spec。
- Spec Coding唔係取代Vibe Coding,而係為佢提供穩定骨架,兩者互補。
- 規範分三層:功能規範(What)、架構規範(How語言無關)、實現規範(How語言相關),有效防止意圖丟失同結果漂移。
- 先規範、後實現、再驗證嘅流程大幅降低返工成本,通知系統示例展示具體做法。
- 常見陷阱包括規範只寫happy path、將實現細節誤寫為需求、只生成不驗證;最佳實踐係規範與代碼同版本管理、規範變更觸發自動測試。
- 可執行路徑:探索期用Vibe Coding快速驗證,沉澱期抽成specs,重建期基於規範實現並強制測試審查。
Claude Code 規範驅動實現指令
在 Claude Code 中使用 @specs/notification.md 按規範實現通知系統,嚴格分 4 步執行:1) 先輸出 plan;2) 按任務逐步實現;3) 每步都運行測試;4) 完成後做一次 code review 並列出風險項。
Vibe Coding 嘅生產困境
Vibe Coding 嘅優勢係快,尤其適合探索期。但佢喺生產階段成日出現幾個核心問題。
- 意圖丟失:Prompt淨係保留「點樣做」,好難沉澱「點解咁做」。
- 結果漂移:多人協作時,每個人嘅Prompt風格唔同,輸出標準唔一致。
- 驗證滯後:先寫code再補測試,返工成本高。
- 經驗不可複用:一次性對話好難遷移到下一個項目。
意圖丟失、結果漂移、驗證滯後、經驗不可複用
Spec Coding 三層規範模型
關鍵係畀Vibe一個穩定嘅骨架——Spec Coding。將規範當成source of truth,代碼只係實現產物。
三層規範模型
- 1 功能規範(What):定義用戶故事、驗收標準、邊界條件。
- 2 架構規範(How - 語言無關):定義數據模型、API契約、安全約束、性能目標。
- 3 實現規範(How - 語言相關):定義技術棧版本、測試框架、編碼約束、發佈要求。
喺Claude Code入面,對應關係係:CLAUDE.md做項目級總規範,.claude/rules/做分層規則,specs/*.md做功能級規範,/plan將規範拆成可執行任務。
CLAUDE.md、.claude/rules/、specs/*.md
實戰示範:通知系統
下面係一套最小可運行樣例,體現「先規範,後實現,再驗證」嘅流程。
# 用戶通知系統規範
## 功能需求
1. 支持站內通知、郵件通知、推送通知
2. 通知類型:系統公告、訂單狀態、促銷活動、安全提醒
3. 支持已讀/未讀、批量已讀
4. 用戶可按渠道與類型配置偏好
## 數據模型
- notifications: id, userId, type, channel, title, content, isRead, createdAt
- preferences: userId, type, channel, enabled
## 驗收標準
- 未讀數在標記已讀後實時正確
- 批量已讀後用戶未讀數為 0
- 偏好變更後立即生效
## 非功能性要求
- 所有輸入參數必須校驗
- 錯誤要有明確錯誤碼
- 關鍵行為可測試
跟住根據規範實現 src/notification-service.ts,再寫 tests/notification-service.test.ts 驗證。運行測試用 npx vitest run。
嚴格拉4步執行:先出plan,逐任務實現,每步跑測試,完成後code review並列風險項
specs/notification.md、src/notification-service.ts、tests/notification-service.test.ts
最佳實踐與常見陷阱
最佳實踐包括以下幾點。
規範與代碼同倉庫同版本管理
- 規範與代碼同倉庫同版本管理,PR同時審查。
- 每個規範都帶驗收標準和非功能性要求。
- 規範變更必須觸發自動化測試,防止實現漂移。
常見陷阱就要小心呢啲。
規範只寫happy path、將實現細節誤寫成需求、只生成不驗證
- 規範只寫happy path,漏咗錯誤處理同邊界條件。
- 將「實現細節」誤寫成「需求事實」,令規範過早僵化。
- 淨係叫AI生成,唔做驗證,當草稿做成品。
規範本質就係代碼,唔係文檔附屬品
從Vibe到Spec:漸進遷移路徑
唔需要一刀切,可以分3個階段逐步過渡。
探索期、沉澱期、重建期
- 1 第1階段(探索):用Vibe Coding喺30分鐘內驗證諗法係咪成立。
- 2 第2階段(沉澱):將有效結論抽成specs/*.md,補齊驗收同約束。
- 3 第3階段(重建):基於規範重做生產實現,並強制測試與審查。
一句話收尾:規範唔係文檔附屬品,規範本身就係代碼。如果你都將AI從「會寫」推到「可交付」,歡迎留言分享你團隊最卡嘅位。
規範本身係代碼
Code is a lossy projection of intent.
代碼係意圖嘅有損投影。
呢句話嘅價值在於,佢將好多團隊而家遇到嘅問題講得好清楚:
我哋唔係唔識「寫代碼」,而係成日冇將「點解咁寫」變成可以驗證、可以協作、可以演進嘅規範。
本文預設你用 Node.js 20 + TypeScript + Vitest + Claude Code,目標唔係做一個 Demo,而係將 AI 輔助開發提升到生產級別。
問題分析:Vibe Coding 點解成日喺後期失速
Vibe Coding 嘅優點係快,尤其適合探索期。但佢喺生產階段成日有4個問題:
1. 意圖流失:Prompt 只保留「點做」,好難沉澱「點解咁做」。 2. 結果漂移:多人協作嗰陣,每個人嘅 Prompt 風格唔同,輸出標準唔一致。 3. 驗證滯後:先寫代碼再補測試,返工成本高。 4. 經驗唔可以重用:一次性對話好難搬去下一個項目。
所以關鍵唔係否定 Vibe,而係俾佢一個更穩定嘅「骨架」——Spec Coding。

解決方案:Spec Coding 嘅三層規範模型
將規範當成 source of truth,代碼當成實現產物。推薦用三層結構:
1. 功能規範(What)
定義用戶故事、驗收標準、邊界條件。2. 架構規範(How - 語言無關)
定義數據模型、API 契約、安全限制、性能目標。3. 實現規範(How - 語言相關)
定義技術棧版本、測試框架、編碼限制、發佈要求。
喺 Claude Code 入面,對應關係通常係:
• CLAUDE.md:項目級總規範• .claude/rules/:分層規則• specs/*.md:功能級規範• /plan:將規範拆成可執行任務

實戰例子:用規範驅動通知系統(可運行)
下面俾一套最小可運行樣例,體現「先規範,後實現,再驗證」。
步驟1:先寫 specs/notification.md
# 用戶通知系統規範
## 功能需求
1. 支持站內通知、郵件通知、推送通知
2. 通知類型:系統公告、訂單狀態、促銷活動、安全提醒
3. 支持已讀/未讀、批量已讀
4. 用戶可按渠道與類型配置偏好
## 數據模型
- notifications: id, userId, type, channel, title, content, isRead, createdAt
- preferences: userId, type, channel, enabled
## 驗收標準
- 未讀數在標記已讀後實時正確
- 批量已讀後用戶未讀數為 0
- 偏好變更後立即生效
## 非功能性要求
- 所有輸入參數必須校驗
- 錯誤要有明確錯誤碼
- 關鍵行為可測試步驟2:實現 src/notification-service.ts
export type NotificationType = 'system' | 'order' | 'promo' | 'security'
export type Channel = 'in_app' | 'email' | 'push'
export interface Notification {
id: string
userId: string
type: NotificationType
channel: Channel
title: string
content: string
isRead: boolean
createdAt: Date
}
interface PreferenceKey {
userId: string
type: NotificationType
channel: Channel
}
export class NotificationService {
private notifications: Notification[] = []
private preferences = new Map<string, boolean>()
constructor(seed: Notification[] = []) {
this.notifications = [...seed]
}
// 統一生成偏好鍵,避免拼接邏輯分散
private prefKey(key: PreferenceKey): string {
return `${key.userId}:${key.type}:${key.channel}`
}
// 更新通知偏好,默認 true 表示開啓
updatePreference(input: PreferenceKey, enabled: boolean): void {
this.preferences.set(this.prefKey(input), enabled)
}
// 判斷某類通知渠道是否開啓
isEnabled(input: PreferenceKey): boolean {
const value = this.preferences.get(this.prefKey(input))
return value ?? true
}
// 獲取用戶通知,支持按已讀狀態過濾
list(userId: string, isRead?: boolean): Notification[] {
return this.notifications.filter((n) => {
if (n.userId !== userId) return false
if (typeof isRead === 'boolean' && n.isRead !== isRead) return false
return true
})
}
// 標記單條已讀,不存在時拋錯,方便上層返回 404
markAsRead(userId: string, notificationId: string): void {
const target = this.notifications.find(
(n) => n.userId === userId && n.id === notificationId
)
if (!target) throw new Error('NOTIFICATION_NOT_FOUND')
target.isRead = true
}
// 批量標記已讀
markAllAsRead(userId: string): number {
let affected = 0
for (const n of this.notifications) {
if (n.userId === userId && !n.isRead) {
n.isRead = true
affected += 1
}
}
return affected
}
// 未讀數用於前端角標展示
unreadCount(userId: string): number {
return this.notifications.filter((n) => n.userId === userId && !n.isRead).length
}
}步驟3:驗證 tests/notification-service.test.ts
import { describe, expect, it } from 'vitest'
import { NotificationService } from '../src/notification-service'
const seed = [
{
id: 'n1',
userId: 'u1',
type: 'system',
channel: 'in_app',
title: '系統維護',
content: '今晚 22:00 維護',
isRead: false,
createdAt: new Date('2026-04-09T10:00:00Z')
},
{
id: 'n2',
userId: 'u1',
type: 'promo',
channel: 'email',
title: '限時活動',
content: '全場 8 折',
isRead: false,
createdAt: new Date('2026-04-09T11:00:00Z')
}
] as const
describe('NotificationService', () => {
it('markAsRead 後未讀數應減少', () => {
const service = new NotificationService([...seed])
expect(service.unreadCount('u1')).toBe(2)
service.markAsRead('u1', 'n1')
expect(service.unreadCount('u1')).toBe(1)
})
it('markAllAsRead 後未讀應為 0', () => {
const service = new NotificationService([...seed])
const affected = service.markAllAsRead('u1')
expect(affected).toBe(2)
expect(service.unreadCount('u1')).toBe(0)
})
it('偏好關閉後 isEnabled 應返回 false', () => {
const service = new NotificationService([...seed])
service.updatePreference(
{ userId: 'u1', type: 'promo', channel: 'email' },
false
)
expect(
service.isEnabled({ userId: 'u1', type: 'promo', channel: 'email' })
).toBe(false)
})
})運行方式:
npm i -D typescript vitest tsx
npx vitest run喺 Claude Code 入面嘅推薦指令
@specs/notification.md
請按規範實現通知系統,嚴格分 4 步執行:
1) 先輸出 plan;
2) 按任務逐步實現;
3) 每步都運行測試;
4) 完成後做一次 code review 並列出風險項。呢條指令嘅重點係:將「生成代碼」變成「執行規範 + 驗證規範」。
最佳實踐與常見陷阱
最佳實踐:
1. 規範同代碼同倉庫同版本管理,PR 同時審查。 2. 每個規範都帶驗收標準同非功能性要求。 3. 規範變更需要觸發自動化測試,防止實現漂移。
常見陷阱:
1. 規範只寫 happy path,漏咗錯誤處理同邊界條件。 2. 將「實現細節」誤寫成「需求事實」,導致規範過早僵化。 3. 淨係俾 AI 生成,唔做驗證,將草稿當成品。
漸進遷移:從 Vibe 到 Spec 嘅可執行路徑
1. 第1階段(探索)
用Vibe Coding喺30分鐘內驗證想法係咪成立。2. 第2階段(沉澱)
將有效結論抽成specs/*.md,補齊驗收同限制。3. 第3階段(重建)
基於規範重新做生產實現,並強制測試同審查。
呢條路徑嘅核心係:速度來自 Vibe,質量來自 Spec。

總結
Spec Coding 唔係取代 Vibe Coding,而係將 AI 編程由「識寫」推到「可交付」。
當你開始認真維護 CLAUDE.md、rules、specs,你已經喺度做工程化嘅 AI 開發。
一句話收尾:
規範唔係文檔附屬品,規範本身就係代碼。
如果你都將 AI 由「識寫」推到「可交付」,歡迎留言講嚇你團隊而家最頭痛係規範、測試,定係協作流程。
參考資料
• 從 Vibe Coding 到 Spec Coding:AI 編程嘅進化之路
2026.04.09 17:26
滬 · 趙巷KFC
📌 聲明:本文由 AI 輔助完成
Code is a lossy projection of intent.
代碼是意圖的有損投影。
這句話的價值在於,它把很多團隊正在經歷的問題說透了:
我們不是不會“寫代碼”,而是經常沒有把“為什麼這樣寫”變成可驗證、可協作、可演進的規範。
本文默認你使用 Node.js 20 + TypeScript + Vitest + Claude Code,目標不是做一個 Demo,而是把 AI 輔助開發拉到生產級。
問題分析:Vibe Coding 為什麼總在後期失速
Vibe Coding 的優勢是快,尤其適合探索期。但它在生產階段常見 4 個問題:
1. 意圖丟失:Prompt 只保留“怎麼做”,很難沉澱“為什麼這樣做”。 2. 結果漂移:多人協作時,每個人的 Prompt 風格不同,輸出標準不一致。 3. 驗證滯後:先寫代碼再補測試,返工成本高。 4. 經驗不可複用:一次性對話很難遷移到下一個項目。
所以關鍵不是否定 Vibe,而是給它一個更穩定的“骨架”——Spec Coding。

解決方案:Spec Coding 的三層規範模型
把規範當成 source of truth,代碼當成實現產物。推薦用三層結構:
1. 功能規範(What)
定義用戶故事、驗收標準、邊界條件。2. 架構規範(How - 語言無關)
定義數據模型、API 契約、安全約束、性能目標。3. 實現規範(How - 語言相關)
定義技術棧版本、測試框架、編碼約束、發佈要求。
在 Claude Code 中,對應關係通常是:
• CLAUDE.md:項目級總規範• .claude/rules/:分層規則• specs/*.md:功能級規範• /plan:把規範拆成可執行任務

實戰示例:用規範驅動通知系統(可運行)
下面給一套最小可運行樣例,體現“先規範,後實現,再驗證”。
步驟 1:先寫 specs/notification.md
# 用戶通知系統規範
## 功能需求
1. 支持站內通知、郵件通知、推送通知
2. 通知類型:系統公告、訂單狀態、促銷活動、安全提醒
3. 支持已讀/未讀、批量已讀
4. 用戶可按渠道與類型配置偏好
## 數據模型
- notifications: id, userId, type, channel, title, content, isRead, createdAt
- preferences: userId, type, channel, enabled
## 驗收標準
- 未讀數在標記已讀後實時正確
- 批量已讀後用戶未讀數為 0
- 偏好變更後立即生效
## 非功能性要求
- 所有輸入參數必須校驗
- 錯誤要有明確錯誤碼
- 關鍵行為可測試步驟 2:實現 src/notification-service.ts
export type NotificationType = 'system' | 'order' | 'promo' | 'security'
export type Channel = 'in_app' | 'email' | 'push'
export interface Notification {
id: string
userId: string
type: NotificationType
channel: Channel
title: string
content: string
isRead: boolean
createdAt: Date
}
interface PreferenceKey {
userId: string
type: NotificationType
channel: Channel
}
export class NotificationService {
private notifications: Notification[] = []
private preferences = new Map<string, boolean>()
constructor(seed: Notification[] = []) {
this.notifications = [...seed]
}
// 統一生成偏好鍵,避免拼接邏輯分散
private prefKey(key: PreferenceKey): string {
return `${key.userId}:${key.type}:${key.channel}`
}
// 更新通知偏好,默認 true 表示開啓
updatePreference(input: PreferenceKey, enabled: boolean): void {
this.preferences.set(this.prefKey(input), enabled)
}
// 判斷某類通知渠道是否開啓
isEnabled(input: PreferenceKey): boolean {
const value = this.preferences.get(this.prefKey(input))
return value ?? true
}
// 獲取用戶通知,支持按已讀狀態過濾
list(userId: string, isRead?: boolean): Notification[] {
return this.notifications.filter((n) => {
if (n.userId !== userId) return false
if (typeof isRead === 'boolean' && n.isRead !== isRead) return false
return true
})
}
// 標記單條已讀,不存在時拋錯,方便上層返回 404
markAsRead(userId: string, notificationId: string): void {
const target = this.notifications.find(
(n) => n.userId === userId && n.id === notificationId
)
if (!target) throw new Error('NOTIFICATION_NOT_FOUND')
target.isRead = true
}
// 批量標記已讀
markAllAsRead(userId: string): number {
let affected = 0
for (const n of this.notifications) {
if (n.userId === userId && !n.isRead) {
n.isRead = true
affected += 1
}
}
return affected
}
// 未讀數用於前端角標展示
unreadCount(userId: string): number {
return this.notifications.filter((n) => n.userId === userId && !n.isRead).length
}
}步驟 3:驗證 tests/notification-service.test.ts
import { describe, expect, it } from 'vitest'
import { NotificationService } from '../src/notification-service'
const seed = [
{
id: 'n1',
userId: 'u1',
type: 'system',
channel: 'in_app',
title: '系統維護',
content: '今晚 22:00 維護',
isRead: false,
createdAt: new Date('2026-04-09T10:00:00Z')
},
{
id: 'n2',
userId: 'u1',
type: 'promo',
channel: 'email',
title: '限時活動',
content: '全場 8 折',
isRead: false,
createdAt: new Date('2026-04-09T11:00:00Z')
}
] as const
describe('NotificationService', () => {
it('markAsRead 後未讀數應減少', () => {
const service = new NotificationService([...seed])
expect(service.unreadCount('u1')).toBe(2)
service.markAsRead('u1', 'n1')
expect(service.unreadCount('u1')).toBe(1)
})
it('markAllAsRead 後未讀應為 0', () => {
const service = new NotificationService([...seed])
const affected = service.markAllAsRead('u1')
expect(affected).toBe(2)
expect(service.unreadCount('u1')).toBe(0)
})
it('偏好關閉後 isEnabled 應返回 false', () => {
const service = new NotificationService([...seed])
service.updatePreference(
{ userId: 'u1', type: 'promo', channel: 'email' },
false
)
expect(
service.isEnabled({ userId: 'u1', type: 'promo', channel: 'email' })
).toBe(false)
})
})運行方式:
npm i -D typescript vitest tsx
npx vitest run在 Claude Code 中的推薦指令
@specs/notification.md
請按規範實現通知系統,嚴格分 4 步執行:
1) 先輸出 plan;
2) 按任務逐步實現;
3) 每步都運行測試;
4) 完成後做一次 code review 並列出風險項。這條指令的重點是:把“生成代碼”變成“執行規範 + 驗證規範”。
最佳實踐與常見陷阱
最佳實踐:
1. 規範與代碼同倉庫同版本管理,PR 同時審查。 2. 每個規範都帶驗收標準和非功能性要求。 3. 規範變更必須觸發自動化測試,防止實現漂移。
常見陷阱:
1. 規範只寫 happy path,漏掉錯誤處理與邊界條件。 2. 把“實現細節”誤寫成“需求事實”,導致規範過早僵化。 3. 只讓 AI 生成,不做驗證,把草稿當成成品。
漸進遷移:從 Vibe 到 Spec 的可執行路徑
1. 第 1 階段(探索)
用Vibe Coding在 30 分鐘內驗證想法是否成立。2. 第 2 階段(沉澱)
把有效結論抽成specs/*.md,補齊驗收與約束。3. 第 3 階段(重建)
基於規範重做生產實現,並強制測試與審查。
這個路徑的核心是:速度來自 Vibe,質量來自 Spec。

總結
Spec Coding 不是替代 Vibe Coding,而是把 AI 編程從“會寫”推進到“可交付”。
當你開始認真維護 CLAUDE.md、rules、specs,你已經在做工程化的 AI 開發。
一句話收尾:
規範不是文檔附屬品,規範本身就是代碼。
如果你也在把 AI 從“會寫”推進到“可交付”,歡迎留言說說你團隊現在最卡的是規範、測試,還是協作流程。
參考資料
• 從 Vibe Coding 到 Spec Coding:AI 編程的進化之路
2026.04.09 17:26
滬 · 趙巷KFC
📌 聲明:本文由 AI 輔助完成