OpenSpec 讓 SDD 變簡單的三個指令
在前一篇文章我們介紹了 SDD(Spec-Driven Development)的方法論,簡單的說,SDD 的重點就是在寫程式之前先把「規格」定義清楚,讓我們跟 AI 對「什麼叫做完成」有共同的認知,這樣到時候做出來的東西才不會雞同鴨講。
雖然方法論介紹完了,但從理論到實務的 Gap 其實不小,如果要自己手寫規格可能有點辛苦,對新手來說一開始可能也不知道怎麼下手。還好有很多厲害的大大幫我們做了方便的工具,讓我們可以透過幾個簡單的指令就能建立規格並套用在專案裡面。
同樣在上篇文章裡提到了幾個 SDD 工具,像是 Amazon 的 Kiro、GitHub 的 Spec Kit、Tessl,還有一個叫 OpenSpec 的輕量級工具。這篇文章要介紹的就是 OpenSpec,在我看來它可能是目前最容易上手、最適合在現有專案導入的 SDD 工具,我自己現在也每天都在用它。
怕內容太長大家沒耐心看完,基本上 OpenSpec 就三招:proposal → apply → archive
本文推薦的 OpenSpec 不一定適合每個人,請依照自己的需求和情境做選擇。
為什麼是 OpenSpec?
再提醒一次,選擇工具之前,一定要先確定目前自己的情況,不要因為我跟你說好用就跟著用,同時也要知道自己在用工具解決什麼問題。
如果是一個全新的專案,從零開始規劃架構,Kiro 或 Spec Kit 可能會是不錯的選擇。它們的流程比較完整,從需求到設計到實作都有對應的文件結構,適合在專案一開始就建立好規範。但現實情況是大多數我們的工作都不是從零開始,而是在現有系統上加功能、改行為、修 bug。這種已經有一堆程式碼的專案,我們要在這片「褐色的田地(Brownfield)」上繼續耕作。
OpenSpec 的設計理念就是 brownfield-first。它不只適合 0 到 1 的新專案,更適合 1 到 n 的持續開發。它能把「目前系統長什麼樣子」和「我們想要做什麼改變」分開管理,讓每次變更的範圍都很清楚。
先說,並不是 Spec Kit 不好,剛好相反,是它對我來說可能「太好」了。很多時候我只是要修個小功能,Spec Kit 還是很熱心的幫我建立一大堆文件,有種只是想修一顆螺絲卻要寫十幾頁報告的感覺,這也是我選擇 OpenSpec 的原因之一。OpenSpec 不需要 API key,不需要連接雲端服務,所有東西都是 Markdown 檔案,產出的文件不會像 Spec Kit 那麼驚人,檔案都會放在專案的特定目錄裡。安裝完就能用,沒有什麼複雜的設定。
OpenSpec 支援的 AI 工具很廣,OpenAI Codex、Claude Code、Cursor、GitHub Copilot、Gemini CLI 這些主流工具都有支援。如果我們用的工具不在支援清單裡,它也提供 AGENTS.md 的方式,讓任何能讀取檔案的 AI 工具都能理解 OpenSpec 的工作流程。
對 Spec Kit 有興趣的,可參閱朋友們寫的文章:
在既有系統上的價值
在上一篇文章提到 SDD 適合的三種場景,分別是全新專案、探索性開發、以及漸進式改進。就我實際開發專案的經驗來說,漸進式改進可能是 SDD 最能發揮價值的地方。
為什麼?因為既有系統通常有很多隱藏或是前人留下來的「資產」,這些東西不一定寫在文件裡,可能只存在於某個資深工程師的腦袋中,或是散落在各種 commit message 和 PR 討論裡。如果我們直接跟 AI 說「幫我加一個新功能」,AI 不知道這些脈絡,很容易改壞其他地方。改壞了直接噴個 HTTP 500 還算好處理,最怕的是改了之後看起來能動,但其實破壞了某個我們沒注意到的隱性規則。
OpenSpec 的 specs 和 changes 分離設計就是為了處理這個問題。specs 目錄裡面放的是「目前系統的真相」,也就是已經實作完成、部署上線的功能規格。changes 目錄則是「我們想要做的變更」。當我們建立一個提案的時候,它會要求我們列出 Affected specs 和 Affected code。雖然這個動作看起來只是在填表格,但其實是在強迫我們思考「這次變更會影響什麼」。如果我們自己都列不出來,AI 更不可能知道。
不過 Brownfield 場景有個前提,就是我們得先有 specs 可以錨定。如果現有系統本來就沒有規格文件,我們得先花一點時間把現有行為記錄下來,這是一開始的導入成本。比較務實的做法是漸進式導入,先從新功能開始建立 specs,慢慢累積,而不是一開始就想把整個系統的規格都補齊,事實上想要一開始就想把規格補齊也不切實際。
安裝與初始化
OpenSpec 是一個 npm 套件,所以安裝方式很簡單(我假設你看到這裡應該知道怎麼安裝 Node.js),只要一行就搞定:
npm install -g @fission-ai/openspec@latest
安裝完之後,可以用 openspec --version 確認版本。接著切換到專案目錄,執行第一次的初始化:
cd my-project
openspec init
執行之後會看到這個畫面:

第一次初始化會問我們在用哪些 AI 工具。選完之後,OpenSpec 會自動幫我們設定好對應的指令(Slash Command)。本篇文章我使用 Claude Code 做為示範,但其它的工具也是類似的流程。選完使用的工具之後別急著開始,仔細看,OpenSpec 會提醒你把一小段文字要請你貼到你的 Coding Agent 裡:
1. Populate your project context:
"Please read openspec/project.md and help me fill it out
with details about my project, tech stack, and conventions"
2. Create your first change proposal:
"I want to add [YOUR FEATURE HERE]. Please create an
OpenSpec change proposal for this feature"
3. Learn the OpenSpec workflow:
"Please explain the OpenSpec workflow from openspec/AGENTS.md
and how I should work with you on this project"
這段內容主要是讓 AI 了解專案背景,這會在 CLAUDE.md 或 AGENTS.md 生成一小段提示詞,讓你正在使用的 Coding Agent 知道 OpenSpec 的工作流程長什麼樣子或是應該怎麼生成提案。所以怎麼做?很簡單,就打開你的 Coding Agent,然後把這段提示詞貼給它就行了。
現在專案的結構大概長這樣:
├── AGENTS.md
├── CLAUDE.md
└── openspec
├── AGENTS.md # 給 AI 讀的工作流程說明
├── changes # 變更提案
│ └── archive # 已完成的變更
├── project.md # 專案的基本資訊、開發用的技術和慣例
└── specs # 目前系統的規格(真相的來源)
特別關注一下 openspec 目錄,這是 OpenSpec 的重點。openspec/project.md 裡面我們可以填寫專案的技術棧、寫作慣例、重要限制等等資訊,讓 AI 在建立提案的時候能參考這些脈絡。openspec/specs/ 目錄則是放目前系統的規格文件。初始化完成後,建議可以自己填寫一下 openspec/project.md,把專案的技術棧、寫作慣例、重要限制這些資訊寫進去。這樣 AI 在幫我們建立提案的時候,就會參考這些脈絡,產出更貼近專案風格的內容。
整個 openspec 目錄都應該進版控。specs 是系統的真相來源,changes 讓團隊成員看到進行中的提案,archive 則保留變更歷史。如果不進版控,後面提到的「多人協作」和「歷史紀錄保留」就沒有意義了。
三階段工作流程
OpenSpec 的工作流程分成三個階段,每個階段都有對應的指令
Stage 1:Draft Proposal(草擬提案)
當我們想要新增功能、做破壞性變更、或是改動架構的時候,第一步是建立一個變更提案。觸發方式有幾種。如果用的是 Claude Code,可以直接輸入:
/openspec:proposal 新增使用者搜尋功能
如果用的工具不支援這些指令的話,也可以用自然語言:
幫我建立一個 OpenSpec 提案,我想新增使用者搜尋功能
AI 會在 openspec/changes/ 底下建立一個新的目錄,裡面包含這些檔案:
proposal.md說明這次變更的原因和影響範圍,格式包含 Why、What Changes、Impact 三個區塊。tasks.md則是實作的待辦清單,會用 checkbox 格式列出每個步驟。specs/子目錄,裡面放的是這次變更會影響到的規格差異,這個等一下會詳細說明。
提案建立完之後,記得執行驗證:
openspec validate add-user-search --strict
為什麼要驗證?因為 AI 不是總是那麼乖,生成的規格有時候不一定有符合 OpenSpec 的格式要求。驗證會檢查每個需求是否都有對應的 Scenario、需求描述是否包含 SHALL 或 MUST 關鍵字、Delta 格式是否正確等等。如果現在不抓出格式問題,等到歸檔的時候才發現規格合併失敗,那就比較麻煩了。
Stage 2:Implement(實作)
提案確認沒問題之後,就可以開始實作了:
/openspec:apply add-user-search
這時候 AI 會讀取 proposal.md 和 tasks.md,然後按照任務清單一個一個完成。每完成一個任務,它會把 - [ ] 改成 - [x],這樣我們隨時都能看到進度。這個階段的重點是 AI 的實作必須符合 proposal.md 裡定義的範圍。如果它想做一些規格裡沒寫的東西,我們可以提醒它「這不在提案範圍內,請專注在 tasks.md 列出的項目。」
這就是 SDD 的核心價值。因為規格是在實作之前就寫好的,所以我們有一個明確的標準來驗收 AI 的產出。不會像 Vibe Coding 那麼隨意,做到一半才發現 AI 理解的需求跟我們想的不一樣。
如果實作到一半發現規格有問題怎麼辦?沒關係,在歸檔之前都還可以調整。直接修改 proposal.md、tasks.md 或 specs/ 下的檔案,改完再跑一次 openspec validate 確認格式正確,然後繼續實作就好。這也是 specs 和 changes 分離設計的好處:在歸檔之前都還有調整空間。不會改怎麼辦?可以跟 AI 用自然語言講,它可以幫你改,我大部份時候也都是這樣做的。
Stage 3:Archive(歸檔)
所有任務都完成、測試也通過之後,最後一步是歸檔:
/openspec:archive add-user-search
歸檔會做兩件事。首先會把 changes/add-user-search/ 這整個目錄移到 changes/archive/ 底下,並且在目錄名稱前面加上日期,變成類似 2026-01-15-add-user-search/ 這樣的格式。然後把這次變更的規格差異合併回 specs 目錄。這樣一來,在 specs 目錄裡面永遠是系統目前應該有的行為,而 archive 裡面則保留了每次變更的歷史紀錄。
specs 和 changes 的分離設計
前面提過 specs 和 changes 的分離設計如何幫助我們在既有系統上工作。除了釐清變更邊界之外,這個設計還帶來幾個好處。
- 變更範圍一目瞭然。每次變更會影響哪些功能,直接看
changes/[name]/specs/裡面有哪些檔案就知道了。不用去猜、不用去追 git diff。 - 多人協作更順暢。如果兩個人同時在做不同的功能,他們的提案會在不同的
changes子目錄裡。只要影響的specs不重疊,就不會互相踩到。 - 歷史紀錄完整保留。每次變更歸檔之後都會留在
archive裡面。也許過了三個月有人問「當初為什麼要加這個功能」,我們可以直接去archive裡面找當時的proposal.md來看。
不需要建立提案的情況
是說,不是所有改動都需要走完整的 OpenSpec 流程,以下情況可以直接改程式碼:
- 修 bug。修 bug 是讓程式碼符合規格,不是改變規格,所以不需要建立提案。例如規格寫「登入失敗應回傳 401」,但程式實際回傳 500,這就是 bug,修正它是讓程式符合規格。當然,如果修復方式比較複雜或想留下決策紀錄,開 proposal 也沒問題。
- 修錯字、調整格式、改註解。這些改動不影響系統行為,不需要走 SDD 流程。
- 更新依賴套件(非破壞性)。只要不是 breaking change,更新套件版本不需要建立提案。
- 調整設定。只要設定的改動不會改變系統的行為規格,就不需要。
- 新增現有行為的測試。規格已經定義了行為,補上測試只是驗證實作正確,不需要改規格。
簡單來說,判斷標準就是這次改動會不會讓系統的行為跟 specs 裡面定義的不一樣。如果會,就需要建立提案。如果不會,直接動手改就好。
常用指令
OpenSpec 的指令不多,整理幾個常用的:
openspec list列出目前所有進行中的變更。可以加--specs參數來列出現有的規格。openspec show [name]顯示某個變更或規格的詳細內容。可以加--json來取得 JSON 格式的輸出,方便程式處理。openspec validate [name]驗證某個變更的格式是否正確。建議加上--strict參數做更完整的檢查。openspec archive [name]歸檔某個已完成的變更。可以加--yes跳過確認提示,在自動化流程裡特別好用。openspec view開啟互動式的 dashboard,可以瀏覽所有 specs 和 changes。
如果驗證失敗,可以用 openspec show [name] --json --deltas-only 來看 Delta 的解析結果,找出格式錯誤在哪裡。
其實看到這裡你就可以開始試著用看看 OpenSpec 了,不過如果你對其它細節有興趣的話可以再往下看...
規格的規格
接著我們來細看一下 spec.md 的格式。這是 OpenSpec 最重要的檔案類型,也是最容易出錯的地方。一個典型的 spec.md 大概會長這樣:
# User Authentication Specification
## Purpose
管理使用者的身份驗證和 Session。
## Requirements
### Requirement: User Login
使用者 SHALL 能夠使用 Email 和密碼登入系統。
#### Scenario: 登入成功
- WHEN 使用者輸入正確的 Email 和密碼
- THEN 系統回傳 JWT token
- AND 記錄登入時間
#### Scenario: 密碼錯誤
- WHEN 使用者輸入錯誤的密碼
- THEN 系統回傳 401 錯誤
- AND 增加失敗次數記錄
幾個格式上的重點:
- 每個 Requirement 都必須至少有一個 Scenario。這是 OpenSpec 的規則,驗證的時候會檢查。沒有 Scenario 的需求等於沒有驗收標準,實作的時候就會有模糊空間。
- Scenario 必須用
####開頭。這是很容易搞混的地方,有些時候 AI 會寫成- **Scenario: xxx**或是### Scenario: xxx,這些格式都不對,後續驗證會失敗。 - 需求描述使用
SHALL或MUST。這是從 RFC 2119 借來的慣例,SHALL表示「必須這樣做」,SHOULD表示「建議這樣做」,MAY表示「可以這樣做」。在 OpenSpec 裡面通常用SHALL或MUST來表達規範性的需求。
變更提案裡的 Delta 格式
當我們建立變更提案的時候,changes/[name]/specs/ 底下的檔案不是寫完整的規格,而是寫「跟現有規格相比,這次要改什麼」。這種只描述差異的格式叫做 Delta,用 ## ADDED、## MODIFIED、## REMOVED 這樣的標題來標記每個變更是新增、修改還是刪除。例如我加了一個想要做二階段驗證的功能:
## ADDED Requirements
### Requirement: Two-Factor Authentication
使用者 MUST 在登入時提供第二因素驗證。
#### Scenario: OTP 驗證
- WHEN 使用者輸入正確的密碼
- THEN 系統要求輸入 OTP
... 略 ...
## MODIFIED Requirements
### Requirement: User Login
(這裡放完整修改後的需求內容,包含所有 Scenario)
## REMOVED Requirements
### Requirement: Password Reset via Email
**Reason**: 改用更安全的驗證方式
**Migration**: 改用 SMS 驗證
四種操作的意思很直接。ADDED 是新增的需求,MODIFIED 是修改現有需求,REMOVED 是移除需求,RENAMED 則是重新命名需求。特別要注意 MODIFIED。如果我們要修改一個現有需求,必須把修改後的完整內容貼上去,包含所有的 Scenario。OpenSpec 在歸檔的時候會用我們提供的內容整個取代原本的需求,如果只寫差異的部分,原本的內容就會不見。
什麼時候需要 design.md?
在三個核心檔案(proposal.md、tasks.md、specs/)之外,OpenSpec 還支援一個選用的 design.md。這個檔案用來記錄技術決策,適合在以下情況使用:
- 跨系統或跨模組的變更。如果這次變更會影響多個服務或模組,
design.md可以說明它們之間如何互動。 - 引入新的外部依賴。如果我們要用一個新的套件或服務,
design.md可以解釋為什麼選擇它、考慮過哪些替代方案。 - 有安全性或效能考量。如果這次變更涉及敏感資料或需要特別注意效能,
design.md可以記錄我們的策略。 - 需要 migration。如果這次變更會影響現有資料或需要停機部署,
design.md可以說明 migration 計畫。
design.md 的格式大概是這樣:
## Context
(背景說明、相關的系統脈絡)
## Goals / Non-Goals
- Goals: ...
- Non-Goals: ...
## Decisions
- 決定使用 Redis 做 cache
- 理由:比 Memcached 更適合我們的使用場景
## Risks / Trade-offs
... 略 ...
## Migration Plan
... 略 ...
如果變更比較簡單,沒有複雜的技術決策要做,design.md 可以省略。OpenSpec 不會強制要求這個檔案存在。
一次變更影響多個 Specs
有時候一個變更會影響到多個功能領域。比如說新增雙因素認證,可能同時影響 auth 和 notifications 兩個 specs。這種情況下,changes 底下的 specs/ 目錄會有多個子目錄:
openspec/changes/add-2fa/
├── proposal.md
├── tasks.md
└── specs/
├── auth/
│ └── spec.md # 雙因素認證的需求
└── notifications/
└── spec.md # OTP 通知的需求
auth/spec.md 負責定義雙因素認證的需求,notifications/spec.md 則定義 OTP 發送的規格。兩個檔案各自獨立,但透過同一個 proposal 管理。歸檔的時候,OpenSpec 會把這兩個 Delta 分別合併到對應的 specs 目錄裡。如果 specs/auth/spec.md 或 specs/notifications/spec.md 原本不存在會自動幫我們建立一個。
小結
如果我們已經有在用 Coding Agent 工具,導入 OpenSpec 的成本滿低的,裝一下 npm 套件、跑第一次的初始化再填一下 project.md,然後就可以開始用了。
下次要新增功能的時候,試試看不要只跟 AI 說「幫我做一個 XX 功能」,改成用 /openspec:proposal 新增 XX 功能。因為初始化時已經設定好提示詞,AI 會知道要照 OpenSpec 的格式建立提案,加上 Coding Agent 現在越來越厲害,在建立提案的過程還會一直跟使用者進行提問互動,讓我們確認過規格之後再開始實作。
一開始可能會覺得這樣做比較花時間,畢竟多了一個步驟,要先寫規格再寫程式。但用過幾次之後就會發現前面花的時間會在後面省回來。因為規格清楚了,AI 比較不會做錯方向;因為有驗收標準,我們知道什麼時候算「完成」;因為有歷史紀錄,以後維護的人知道當初為什麼這樣設計。就算 AI 把功能做歪了,我們也可以回到修改前的狀況,重新再來一次,或是換另一個 Coding Agent 來做也行。
使用 SDD 的目的不是要讓開發變得更繁瑣,而是要讓我們跟 AI 的協作更可靠、更有可預測性。OpenSpec 提供了一個簡單易用的框架,讓我們可以在現有專案中逐步導入 SDD,享受它帶來的好處 :)