昨天,我們把多人協作的分支規則整理清楚了。
接下來,就開始在 Feature Branch 上做功能吧!
不過開始做功能,不是開了分支就直接往下寫。
在真正動手以前,我們需要先把「功能」定義清楚:
到底要做什麼、做到什麼程度,哪些行為才算正確?
這幾年 AI Coding 越來越熱門,只要幾句自然語言,就可以讓 Coding Agent 幫忙新增功能、修改 API,甚至一次動到好幾個模組。
也因為 Code 生得太快,「規格」這件事又重新受到重視。
這就來到今天的主題:
Spec-Driven Development(SDD,規格驅動開發)。
SDD 當然不是 AI 出現以後才突然被發明的。
但以前需求沒有講清楚,還常常是工程師自己一邊理解、一邊實作;現在如果我只跟 Agent 說:
「幫我把報名功能改成讓大家回報有空時間。」
這句話裡其實還藏著一大堆空白。
「有空時間」是一個時間點,還是一段區間?
一個人可以回報幾段?
資料要怎麼存?
原本的 API 要不要跟著改?
這些事情如果沒有先定義,Agent 還是得替我做出某種判斷。
AI Coding 讓寫 Code 變得更快,也讓「需求到底有沒有說清楚」這件事變得更重要。
我第一次真的開始注意到 SDD,順序其實跟今天寫文章完全相反。
我不是先把理論讀完,再決定:
「好!接下來我要採用 SDD!」
在使用 Spectra 以前,我本來就會透過 Superpowers 這套 Skills,先和 AI 討論需求、整理 Plan、把工作拆成 Task,再一步一步進入實作。
Superpowers 本身不是專門的 SDD 工具;但它讓我很早就開始習慣:
不要只丟一句 Prompt,就直接叫 AI 往 Code 衝。
後來 BuJo 開發開始使用 Spectra。
當時我其實還是不知道:
「喔,原來這背後有一個叫 SDD 的概念。」
只是覺得這個工具很直覺,它會把需求,以及這份規格需要做的變動,一項一項整理成待辦事項,讓整個功能接下來要怎麼做,看起來清楚很多。
直到後期重新回頭查資料,我才發現:
原來我一直在用的這些工具,背後都在處理同一個更底層的問題——開始寫 Code 以前,先把「規格」說清楚。
Spec-Driven Development 最核心的東西,其實就在名字裡:
讓 Specification(規格)成為後續開發的重要依據。
也就是不要從一個模糊需求,直接跳到實作。
而是先慢慢把它變成:
模糊的需求/想法
↓
Specification
系統應該做到什麼?
↓
Design / Plan
準備怎麼做到?
↓
Tasks
實際有哪些工作?
↓
Implementation
開始寫 Code
不同 SDD 工具的實際流程不一定長一樣。
目前常被拿來討論的工具,例如:
可以看到,不同工具會用不同的流程和文件結構來實踐 SDD。
所以這裡真正需要抓住的,不是哪一套流程才是標準答案,而是它們共同在做的事:
先把需求和預期行為整理清楚,再往設計、拆解和實作前進。
第一次開始用 Spectra 的時候,我其實有點被嚇到。
因為原本只是:
「我要改一個功能。」
結果專案裡突然多出好多 Markdown、資料夾、規格檔。
我還一直問 AI:
「這是正常的嗎?為什麼只是改個東西,突然多了這麼多行?」
後來才知道,這些突然多出來的文件,其實是在把這次功能的需求、設計和修改脈絡分開整理、留下來。
像 OpenSpec/Spectra,就會把不同性質的規格資訊分開管理。
以 BuJo 實際使用的結構來看,大概可以先這樣理解:
openspec/
├─ specs/
│ └─ 現在系統應該怎麼運作
│
└─ changes/
├─ 這一次準備修改什麼
│
└─ archive/
└─ 過去做過哪些修改
完成的 Change 會再被放進 archive/,把當時修改的脈絡留下來。
所以那些突然增加的文件,不是在替專案灌水。
原本可能只存在一句:
「我們之前好像有說過這裡要這樣改。」
現在會被拆成:
為什麼要改?
這次要改什麼?
哪些地方會受到影響?
準備怎麼實作?
有哪些 Task?
最後系統應該怎麼運作?
而這些規格檔也會跟著專案一起進版控。
這一點很重要。
因為它代表規格不是只有「我自己的 AI 看過」。
而是可以變成團隊共同留下來的專案資料。
但看到 Spectra 一次多出這麼多規格文件時,我又冒出另一個疑問:
「那我之後每改一個功能,都要跑完整套 SDD 嗎?」
其實不用。
SDD 可以用在一個個功能或變更上,但如果只是非常單純、影響範圍很小的修改,跑完整套規格流程反而可能增加不必要的成本。
比較值得使用的情況,通常有這幾種:
反過來,如果只是改一個 typo、調整間距、換 icon,或非常局部又沒有改變產品行為的 UI 修改,就不一定需要特地跑一套完整的 SDD 流程。
真正要判斷的,不是「這個功能夠不夠大」,而是這次修改裡,有沒有需要先被對齊的需求、行為或決策。
這件事放回 BuJo 的活動情境二最明顯。
我們原本的報名方式,是由活動建立者先列出一個一個候選時段,再讓參與者從裡面勾選。
但這跟我們真正想表達的需求其實有落差。
參與者真正想說的比較像:
「我這一整段時間都有空。」
而不是:
「你列的 A、B、C 三個時段裡,我選 A 跟 C。」
所以後來情境二做了一次完整 redesign:
原本
建立者列出一個個候選時段
↓
參與者勾選
改成
建立者提供可報名的時間範圍
↓
參與者自由回報自己的可用區間
↓
系統計算多人重疊時間
如果只把這件事講成一句:
「把候選時段改成時間區間。」
聽起來好像只是 UI 換了一個選擇方式。
但真的把規格拆開後,連 API 傳進來的資料格式都跟著變了:
原本
{ candidateSlotIds }
改成
{ ranges: [{ start, end }] }
原本傳的是「我選了哪些既有的候選時段」,現在則變成「我有哪些時間區間有空」。
而這個差異也會一路牽動後面的資料模型、報名流程、時間重疊計算,以及最後確認活動時間的方式。
這時候的 Spec,就不再只是一句「情境二改成 Range」。
甚至後續截止時間模型再次調整時,同一份 current spec 還繼續被修改。
這一點是我現在回頭看最喜歡的地方之一:
Spec 並不是先寫完就封印起來。
需求變了,它也可以跟著變。
所以 SDD 不是要求我們在第一天就預知未來,把所有需求一次寫到完美。
它比較像是:
當共識改變時,不只改 Code,也一起更新「現在大家認為正確的是什麼」。
假設今天我直接跟 Coding Agent 說:
「幫我把情境二改成讓大家自己回報有空時間。」
它很可能真的馬上開始動工。
但這句話裡沒講清楚的地方,還是得有人補上答案。
可能是我在下一輪 Prompt 補。
也可能是 Agent 先照自己的理解做,等畫面跑出來之後,我才發現:
「啊!不是這個意思。」
再重新修改。
問題不一定是 AI 寫 Code 寫錯。
有時候真正沒被定義的,是:
「到底怎樣才算做對?」
差別不只是 AI 有沒有收到 Prompt,而是專案有沒有一份比 Prompt 更長壽的共識。
我現在回頭看 SDD,最有感的其實不是多了多少規格文件。
而是當自己的功能和夥伴負責的功能開始彼此交錯之後,我越來越常需要回頭確認:
「這個東西當初為什麼會變成現在這樣?」
如果答案只散落在某一次討論、某個人的記憶,或某一段和 AI 的對話裡,時間一久,很容易連自己都忘記。
但當規格跟著專案一起留下來,我至少知道可以回頭找:
為什麼當初這樣做?
後來又為什麼改掉?
現在系統正確的行為是什麼?
這也讓我慢慢覺得:
一個功能真正值得留下來的,不只有最後那份 Code,還有當初讓它長成現在這個樣子的理由。
而當需求、預期行為和要完成的 Tasks 都定清楚之後,接下來就要正式進入實作。
這時候,我又開始想:
規格都寫清楚了,要怎麼確定接下來寫出來的 Code,真的符合我們原本定義的行為?
下一篇,就來看另一種從「測試」開始推進實作的方法:
Test-Driven Development(TDD,測試驅動開發)。
高見龍|Spec-Driven Development(SDD)
https://kaochenlong.com/sdd-spec-driven-development
高見龍|OpenSpec
https://kaochenlong.com/openspec
高見龍|Spectra 2.0
https://kaochenlong.com/spectra-app-2
Spectra GitHub Repository
https://github.com/kaochenlong/spectra-app
Superpowers GitHub Repository
https://github.com/obra/superpowers