是 Ray 不是 Array

整理這些技術筆記真的很花時間,如果你願意 關閉 Adblock 支持我,我會把這份感謝轉換成更多「踩坑轉避坑」的內容給你!ヽ(・∀・)ノ

Advertisement
2025-12-02 AI

淺談 AI Vibe Coding 當今主流的 BDD 與 SDD

淺談 AI Vibe Coding 當今主流的 BDD 與 SDD

前言

基本上只要你有使用過 AI 來開發任何一個產品或者功能,你應該或多或少都聽過 AI 程式碼生成等詞彙。在這裡,我將這種「直接用自然語言來描述功能,由 AI 生成程式碼」的模式,稱為「Vibe Coding (直覺式開發)」。

Vibe Coding

首先 Vibe Coding 直接翻譯成中文我認為滿奇怪的,但我覺得翻成「直覺式開發」比翻譯成「憑感覺開發」來的好很多,因為 Vibe Coding 的意思就是「直接用直覺來開發」,也就是說你不需要去寫程式碼,你只需要用自然語言來描述你想要的功能,然後 AI 就會幫你生成程式碼。

除此之外 Vibe Coding 也有一些核心精隨:

  • 自然語言描述: 使用自然語言來描述功能,而不是人為地撰寫程式碼。
  • 不在意實作細節: 使用者不需要關心是用 Python 還是 JavaScript,也不用管分號要不要加,全由 AI 處理。
  • 專注業務邏輯: 使用者可以專注於業務邏輯的描述,而不需要關心技術細節。這使得開發者可以快速驗證想法的可行性,將精力放在「解決什麼問題」而非「如何寫程式」。
  • 即時回饋與迭代: 當程式碼 or 功能不符合預期時,可以請 AI 來修改而不是人工調整。
  • 門檻解除: 許多開發者對於寫程式充滿著畏懼,但 Vibe Coding 可以讓這些人也能夠參與開發,因為 Vibe Coding 只需要自然語言描述就可以了,實現真正的「人人都可以創造」。

以我自己來講,我也用 Vibe Coding 做了不少小工具,甚至套用到工作上。

Vibe Coding 隱藏的問題

完全依賴「直覺」與 AI 溝通來產生程式碼,在專案變大的時候,也會開始有一些明顯的瓶頸出現:

  • 不夠精確(Ambiguity): 這是自然語言描述本身就有的模糊性,尤其中文博大精深,同樣一句話可能有多種解釋。
    • 舉例:「請幫我寫一個可以計算兩個數字相加的程式」。
    • 對於 AI 來講:你到底是想要做「浮點數相加」還是「整數相加」呢?因為「數字」這個詞本身就不夠精確
  • 風格不一致(Inconsistency): 開發者為了「未來的維護性」通常會有一套嚴謹的 Code Style,然而 Vibe Coding 生成的程式碼,往往都有可能不同,若人工介入就會失去 Vibe Coding 的意義。
  • 功能缺陷(Bugs): AI 生成的程式碼都有細微的邏輯錯誤,對於一名非技術人員來說,這些錯誤可能很難被發現 or 邊界案例(Corner Cases)無法被完整涵蓋。
  • 資安疑慮(Security): 可能會有一些安全性問題,例如 SQL Injection、XSS 等等,這些問題需要使用者自己去處理。

Note
邊界案例是指在特定條件下,系統可能會出現異常行為的情況,這些情況通常是比較少見或極端的,但卻可能導致系統崩潰或錯誤。

這些都是很明顯的 Vibe Coding 的缺點,所以接下來我就要來介紹 BDD 與 SDD,這兩個方法可以幫助我們解決 Vibe Coding 的一些問題。

BDD 與 SDD:讓 AI 更聽話的框架

BDD 全名是 「行為驅動開發」(Behavior-Driven Development) ,而 SDD 全名是 「規格驅動開發」(Specification-Driven Development) ,這兩個方法都是用來幫助我們更好地描述需求和行為,從而生成更精確和一致的程式碼。

這兩個東西並不是多新潮的東西,以 BDD 來說,它其實跟 TDD(Test-Driven Development)有很大的關係,TDD 核心精髓在於 「先寫測試,後寫程式碼」 ,但 BDD 則是 「先描述行為,後寫程式碼」

「TDD 不適合用於 Vibe Coding 嗎?」你可能這樣問。

我認為這是一個好問題,並不是 TDD 不適合用於 Vibe Coding,而是 TDD 本身就是一個比較技術性的東西,對於非技術人員來說,TDD 可能會有一些門檻,對於一個普通人來說很難理解什麼是「測試」,更不用說他們連「程式碼」可能都不知道是什麼東西了。

而 BDD 則是很剛好的與 Vibe Coding 的理念相符,因為 BDD 是用自然語言來描述行為,這正好符合 Vibe Coding 的「自然語言描述」的特性,其實也是在強迫使用者用結構化的的方式來描述需求。

那…SDD 呢?簡單來講,SDD 就是一份詳細的需求規格文件,這份文件詳細的說明了系統應該要有什麼功能、行為以及限制條件,這樣 AI 在生成程式碼時就可以參考這份文件,從而生成更符合需求的程式碼。

所以接下來我將會拆分兩個部分來介紹,以及說明如何應用在 Vibe Coding 上。

BDD 在 Vibe Coding 的應用實戰:講述故事

BDD 的運作通常圍繞著三個核心步驟,而在 AI 開發中,我們主要專注於第一步:

  1. 行為描述(Given-When-Then): 使用者用自然語言來描述系統的行為,通常會使用「Given-When-Then」的格式來描述
  2. 實現行為的測試(Test Implementation): 根據行為描述來實現測試,這些測試可以用來驗證系統是否符合行為描述
  3. 生成程式碼(Code Generation): 最後基於測試案例來生成程式碼

那什麼是 Given-When-Then 呢?

  • Given(前提條件): 描述系統的初始狀態或背景。例如:「Given 使用者已經登入系統」
  • When(行為觸發): 描述使用者或系統所執行的動作。例如:「When 使用者點擊 ‘新增商品’ 按鈕」
  • Then(預期結果): 描述系統應該要達到的結果。例如:「Then 系統應該顯示 ‘商品已新增’ 訊息」

舉例來說,假設我們要開發一個簡單的購物車功能,我們可以用 BDD 來描述這個功能:

1
2
3
Given 使用者進入商品頁面
When 使用者點擊 '加入購物車' 按鈕
Then 系統應該顯示 '商品已加入購物車' 訊息

了解了 BDD 的基本概念後,在這邊我要將 BDD 稍微調整一下,也就是變成以下:

  1. 行為描述(Given-When-Then): 使用 Given-When-Then 來描述系統的行為
  2. 實現行為的測試(Test Implementation): 由 AI 根據行為描述來實現測試
  3. 生成程式碼(Code Generation): 最後由 AI 根據測試案例來生成程式碼

所以其實你真正要專注的點還是在於「行為描述」這個部分,而不是其他兩個部分,因為其他兩個部分都是由 AI 來處理的。

結合 User Story (使用者故事) 提升 AI 理解力

單純的行為描述有時會缺乏上下文(Context),因此我強烈建議搭配 User Story

Note
對於 AI 來講,如果有明確的上下文(Context),它會更容易理解使用者的需求,從而生成更符合需求的程式碼。

User Story 的公式如下:

1
2
我是 < 某種類的使用者 >,我想要 < 做某些事情 > 以達成 < 某種目的 >。
我是 < 某種角色的使用者 >,我想要 < 這些功能 > 以達成 < 某些商業價值 >。

舉例來說,假設我們要開發一個購物車功能,我們可以這樣描述 User Story:

1
我是 一個線上購物的使用者,我想要 能夠將商品加入購物車 以便於 我可以在結帳時一次購買多個商品。

透過前面簡單介紹,我們可以知道 Use Story 可以幫助我們更好地理解使用者的需求,而不是單純的功能面,那為什麼要特別提到 User Story 呢?因為你可以搭配 User Story + BDD 組合成一個給 AI 完美的 Prompt。

如果你直接跟 AI 說你想要做購物車功能,AI 肯定會亂做,但如果你今天是這樣寫:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Requirements

## Requirement 1

User Story: 我是 一個線上購物的使用者,我想要 能夠將商品加入購物車 以便於 我可以在結帳時一次購買多個商品。
1. Given 使用者進入商品頁面 When 使用者點擊 '加入購物車' 按鈕 Then 系統應該顯示 '商品已加入購物車' 訊息
2. Given 使用者已經將商品加入購物車 When 使用者點擊 '查看購物車' 按鈕 Then 系統應該顯示購物車內容
3. Given 使用者在購物車頁面 When 使用者點擊 '結帳' 按鈕 Then 系統應該導向結帳頁面

## Requirement 2

User Story: 我是 一個線上購物的使用者,我想要 能夠從購物車中移除商品 以便於 我可以調整我的購物清單。
1. Given 使用者在購物車頁面 When 使用者點擊 '移除商品' 按鈕 Then 系統應該將該商品從購物車中移除
2. Given 使用者已經移除商品 When 使用者查看購物車 Then 系統應該顯示更新後的購物車內容
3. Given 使用者在購物車頁面 When 使用者點擊 '清空購物車' 按鈕 Then 系統應該將所有商品從購物車中移除

...(以此類推)

你會發現這邊我用了 Requirements 來跟 AI 說明接下來都是一個需求清單,並且搭配了 Markdown 的標題來區分階級,這樣 AI 就可以更好地理解整體的結構,並且生成符合需求的程式碼。

了解完 BDD 後,接下來我們來看看 SDD 怎麼幫助我們解決 Vibe Coding 的問題。

SDD 在 Vibe Coding 的應用:定義骨架

如果說 BDD 是在故事的話,那麼 SDD 就是在畫藍圖,也就是告訴 AI 資料該長什麼樣子、該用什麼技術、有哪些開發規範。

對於一般開發者來講,應該最熟悉的就是 SDD,畢竟 SDD 就是需求規格文件 (Software Design Document) 的縮寫,而需求規格文件通常會包含以下內容:

  • 系統架構(System Architecture): 描述系統的整體架構,包括前端、後端、資料庫等。
  • 資料結構(Data Structures): 定義系統中使用的資料結構,例如資料庫的表格結構、API 的資料格式等。
  • 技術選型(Technology Stack): 指定系統使用的技術,例如程式語言、框架、工具等。
  • 開發規範(Development Standards): 定義程式碼風格、命名規則、測試標準等。
  • 非功能需求(Non-Functional Requirements): 包括效能、安全性、可擴展性等方面的需求。

舉例來講,我們可能會建立一個 Markdown 文件,內容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<!-- design.md -->

## Technology Stack
- **框架:** Nuxt 3
- **語言:** TypeScript
- **資料庫:** Firebase Realtime Database
- **身份驗證:** Firebase Authentication
- **樣式:** Tailwind CSS
- **圖示:** Bootstrap Icons
- **字型:** Google Fonts - Noto Sans TC
- **表單驗證:** VeeValidate
- **日期處理:** Day.js
- **HTTP 請求:** useFetch
- **狀態管理:** Pinia
- **測試:** Jest
- **版本控制:** Git
- **持續整合/持續部署:** GitHub Actions

## Data Structures

### User

interface User {
id: string;
name: string;
email: string;
password: string;
createdAt: Date;
updatedAt: Date;
}

### Product

interface Product {
id: string;
name: string;
description: string;
price: number;
stock: number;
createdAt: Date;
updatedAt: Date;
}

你會發現這些細節相較於 BDD 來說,更加技術性,對於非技術人員來講其實滿不友善的,但對於 AI 跟開發者來講,這些細節卻是非常重要的,也相對容易理解。

建立完 design.md 之後,接著還會建立一個 specs.md,內容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!-- specs.md -->
# Functional Specifications(功能規格)

## Module: Shopping Cart(購物車模組)

### Function: `addToCart`(加入購物車)

**Description:** 負責處理將商品加入購物車的邏輯,包含庫存檢查與狀態更新。

**Inputs (輸入參數):**
- `product`: Product (參考 design.md 定義)
- `quantity`: number (預設為 1)

**Pre-conditions (前置檢核):**
1. `quantity` 必須大於 0。
2. `product.stock` (庫存) 必須大於等於 `quantity`
3. 若庫存不足,拋出錯誤碼 `ERR_OUT_OF_STOCK`

**Process Flow (處理邏輯):**
1. 取得目前的 `CartStore` (Pinia) 狀態。
2. 檢查 `CartStore.items` 中是否已存在該 `product.id`
- **Case A (已存在):**
- 更新該項目的數量:`item.quantity += quantity`
- 檢查更新後的數量是否超過庫存,若超過則設為最大庫存量。
- **Case B (不存在):**
- 建立新的 `CartItem` 物件。
- 設定 `addedAt` 為當前 Timestamp。
- 將新物件 `push``CartStore.items` 陣列。
3. 觸發 `calculateTotal()` 重新計算總金額。
4. 呼叫 `ToastService.success()` 顯示成功訊息。

**Outputs/Side Effects (輸出與副作用):**
- 更新 Pinia `cart` state。
- 不回傳值。

你會發現上面這些內容根本就是等價於程式碼,對於 AI 來講,它也能產出相對應的程式碼,其準確度是非常的高。

如果你用 SDD 寫到這麼細的話,AI 要發生幻覺的機會基本上是很低的,因為它不需要去猜「庫存不足要幹嘛?」,也不用猜「重複加入是要新增一筆還是數量 +1?」,因為你都透過規格文件告訴它了。

所以到底該選 BDD 還是 SDD?

看到這邊應該會充滿著一個疑惑:

「那這樣我該選 BDD 還是 SDD 呢?」

其實兩者是可以混在一起使用的,因為 BDD 側重於「行為描述」,而 SDD 側重於「技術細節」,兩者可以互補。但對於非相關專業人員來說,我會建議先從 BDD 開始,因為 BDD 更符合自然語言的描述方式,對於非技術人員來說更容易上手。

如果本身是有開發經驗的人,那麼就會比較側重於 SDD,畢竟 SDD 可以幫助我們更好地定義系統的技術細節,從而生成更符合需求的程式碼。

當然如果想要 BDD + SDD 混合使用也是可以的,這樣可以同時兼顧「行為描述」與「技術細節」,從而生成更精確和一致的程式碼,而這個範例大概就會變成像這樣:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# Requirements

## Requirement 1: Add to Cart (加入購物車)

### 1. Behavior Description (BDD)

**User Story:**
我是 一個線上購物的使用者,我想要 能夠將商品加入購物車 以便於 我可以在結帳時一次購買多個商品。

**Acceptance Criteria (Gherkin):**
1. **Given** 使用者進入商品頁面 **And** 商品庫存 > 0
**When** 使用者點擊 '加入購物車' 按鈕
**Then** 系統應該顯示 '商品已加入購物車' 訊息
2. **Given** 使用者已經將商品加入購物車
**When** 使用者再次點擊同一商品的 '加入購物車'
**Then** 購物車內的該商品數量應 +1,而不是新增一筆資料
3. **Given** 商品庫存為 0
**When** 使用者查看商品頁面
**Then** '加入購物車' 按鈕應呈現 Disabled 狀態

### 2. Technical Specifications (SDD)
為了滿足上述行為,請嚴格遵守以下技術規範:

- **Data Structure (資料結構):**
- 購物車項目必須符合 `interface CartItem extends Product { quantity: number }`
- `quantity` 欄位預設為 1,且更新時不可超過 `Product.stock`
- **State Management (狀態管理):**
- 使用 Pinia 的 `useCartStore` 進行狀態管理。
- 禁止在 Component 內部直接修改 State,必須透過 Action (`addToCart`) 操作。
- **Logic Flow (邏輯流):**
- 使用 `Array.find` 檢查 `productId` 是否已存在於 Store。
- 若存在:執行 `incrementQuantity()`
- 若不存在:執行 `push()` 新物件。

---

## Requirement 2: Checkout (結帳流程)
...

其實混用並不困難,就是把 BDD 的行為描述放在前面,然後接著 SDD 的技術細節,這樣 AI 就可以同時理解「我要做什麼」以及「我要怎麼做」,從而生成更符合需求的程式碼。

結論

當你這些文件都撰寫的很好的話,你就可以直接下以下 Prompt 給 AI:

1
請參考 @design.md 的技術規範,並實作 @requirements.md 中的 Story 1 與 Story 2。

接著 AI 就會依照你的需求來生成程式碼,而不是亂七八糟的東西。

所以工程師有因此變的輕鬆嗎?我認為沒有,除了維護程式碼之外,還要維護一大票的文件。

整理這些技術筆記真的很花時間,如果你願意 關閉 Adblock 支持我,我會把這份感謝轉換成更多「踩坑轉避坑」的內容給你!ヽ(・∀・)ノ

Advertisement

你的支持會直接轉換成更多技術筆記

如果我的筆記讓你少踩一個坑、節省 Debug 的時間,
也許你可以請我喝杯咖啡,讓我繼續當個不是 Array 的 Ray ☕

buymeacoffee | line | portaly

Terminal

分享這篇文章

留言

© 2025 Ray. All rights reserved.

Powered by Ray Theme