本文探討使用 SDD (規格驅動開發) 可能發生的問題與注意事項。
繼前一篇介紹我的 SDD 實作介紹之後, 我想來探討這種開發方式有什麼可能發生的問題。
但是在那之前, 容我先再次重申我使用的這種程式開發法的目的。
SDD 的核心精神是以「明確、可執行的規格作為開發流程的中心軸」, 將傳統軟體開發中需求不明、實作偏離、測試難對齊等問題, 透過機器可理解的規格文件來一體化管理。它的哲學基礎是「規格即真相」(Spec as the Single Source of Truth)。
依我在第一篇裡面提供的「規格」文件, 由於它要提供的功能太簡單了, 所以這個文件也寫得非常簡單。但是它仍然有上百行 (第一份) 甚至將近兩百行 (第二份) 那麼長。
但是嚴格說起來, 其實即便是這樣鉅細彌遺地寫, 它到底算不算是 SDD, 恐怕還有問號。
如果你把規格寫得太簡單、太籠統, 有人認為那頂多只能算是 PDD (Prompt-Driven Development) 而已, 不能算是 SDD。如果是狹義的 SDD, 它事實上是要求你必須將所有細節盡可能地定義清楚。例如你應該明確定義視窗的長寬是多少個 pixel 或者在哪個數值範圍裡面, 或許還必須要檢查它是否為負值, 諸如此類。
但是若採廣義的 SDD, 那麼 PDD 也算是 SDD。
問題是, 如果你的 prompt 寫得太過於簡單籠統, 它就等於只是普通的 Vibe-Coding 而已。那就完全脫離了 SDD 的意涵。
此外, 由於 LLM 的特性, 如果你的規格寫得不夠明確, 那麼 AI 每次生出來的程式碼都會有或大或小的不同。你的規格寫得愈籠統, 每次產生的結果就愈是不同。而且, 一旦你採用了不同的模型, 或是你在專案裡加入了其它的功能, 都會讓你依據同樣規格做出來的東西每每不同。
根據我多次測試的結果, 即便我原本的規格已經寫得算是詳細了, Copilot 還是有辦法每次都變出略有不同的把戲。即便最後的結果大多數仍然是對的 (其實也有做錯的)。
因此, 如果你不喜歡這種帶有彈性的 Code Generation 風格, 那麼或許你應該再下點功夫在你的規格文件上面。在本文的最後, 我將列出我的規格文件的第三個版本, 主要目的是示範如何在 Markdown 文件裡某些關鍵的地方 (也就是你不希望 Copilot 自由發揮的地方) 放進 YAML 格式的描述。如此一來, 我們就不能說這部份功能的規格「籠統」了。
但話說回來, 你到底要採用什麼策略? 如果你每一種功能的寫法都把它寫死了, 那麼其實你根本不需要 AI 的輔助。你還不如直接寫個 Template Form, 每次都會去複製貼上一份再來改就好。
所以, 我們到底要採用何種策略, 我們應該仔細拿捏。如果你的 Specs 每個細節都寫到 code level 了, 那還叫做 SDD 嗎? 還是 copy & paste 呢?
以下是第三個版本:
---
Title: WinForms 視窗開發規範
Author: Johnny Lee
Version: 1.1
Date Created: 2025-10-26
Last Modified: 2025-10-30
spec:id: winforms.base.v1
---
# WinForms 應用程式視窗開發規範(SDD 版)
本文檔定義了本專案中所有 Windows Forms 視窗(Form)的標準結構、功能與驗證規則。
所有新表單的建立與維護,皆應遵循此規範。程式碼、測試與文件均應以本規格為唯一依據。
---
## 1. 核心原則
| 原則 | 定義 | 可驗證條件 |
|------|------|-------------|
| Responsive | 不可於 UI 執行緒執行 >200ms 的任務 | 檢查所有事件處理器,若方法內呼叫 I/O 或 CPU 任務未使用 async/await,報警告 |
| User-Friendly | >500ms 的任務須提供視覺回饋 | 在長任務開始 500ms 內必須顯示載入提示 |
| Stateful | 記住視窗大小與位置 | Form.Load 與 FormClosing 事件均讀寫 Settings.ini 對應區段 |
| Consistent | 結構一致 | Form 含 MenuBar、StatusBar、Panel 三層結構,命名符合規格 |
---
## 2. 結構與功能規格(Formalized Semantic Block)
```yaml
ui:
components:
- type: ToolStrip
name: MainMenu
must_exist: true
layout:
dock: Top
height_min: 24
style:
back_color: InactiveCaption
items:
- type: ToolStripMenuItem
text: "File"
- type: StatusStrip
name: StatusBar
must_exist: true
layout:
dock: Bottom
height_min: 22
style:
back_color: ScrollBar
items:
- type: ToolStripStatusLabel
- type: ToolStripProgressBar
- type: Panel
name: Content
must_exist: true
layout:
margin: 4
scrollable: true
border_style: FixedSingle
anchors: [Top, Bottom, Left, Right]
state_persistence:
storage_path: "%AppData%/MyApp/Settings.ini"
section: "{FormClassName}"
keys:
PositionX: { type: int, default: 10, range: [0, 4096] }
PositionY: { type: int, default: 10, range: [0, 4096] }
Width: { type: int, default: 800, range: [320, 4096] }
Height: { type: int, default: 600, range: [240, 4096] }
behavior:
load_event: "Form.Load"
save_event: "Form.FormClosing"
helper_class: "IniFile.cs"
async_rules:
max_ui_block_ms: 200
feedback_threshold_ms: 500
require_progress: true
require_cancellation: true
invoke_on_ui_thread: true
performance_rules:
listview_batch_insert_max: 5000
listview_insert_time_limit_ms: 200
require_virtual_mode: true
```
---
## 3. 開發與驗證流程
1. 建立新表單
- Copilot 應詢問開發者要建立的表單名稱 [FormName]。
- 所有 [FormName] 將自動對應 Settings.ini 中的區段。
2. 狀態持久化驗證
- 測試:第一次啟動無設定檔 → 自動生成 [FormName] 區段與預設鍵值。
- 測試:更改位置、大小後關閉 → 再啟動可還原。
- 驗證:使用共用 IniFile.cs 進行讀寫,且路徑為 %AppData%/MyApp/Settings.ini。
3. UI 結構驗證
- 反射檢查 MainMenu, StatusBar, Content 是否存在。
- 驗證 Dock 屬性與 BackColor 值。
- 驗證 Content 的 BorderStyle == FixedSingle 且 AutoScroll == true。
- 在三種不同解析度下,重新調整視窗,確保:
- MainMenu.Top == 0
- StatusBar.Bottom == Form.ClientSize.Height
- Content.Margin == 4
4. 非同步操作驗證
- 執行 >1 秒模擬任務:
- 500ms 內顯示載入提示。
- 可透過 CancellationToken 中止。
- ToolStripProgressBar 更新至 100%。
- UI 在整段任務中仍可互動。
5. 效能驗證
- 模擬 10,000 筆資料加入 ListView:
- 使用 BeginUpdate()/EndUpdate() 包覆。
- 若超過 listview_batch_insert_max,應提示「資料過多」訊息。
6. 佈局方法驗證
- 必有 LayoutControls() 方法並於 OnLayout、Resize 事件呼叫。
- 檢查代碼中避免過度依賴設計工具 Anchor。
---
## 4. 專案共用元件與依賴
|元件 | 功能 | 強制性 |
|------|------|-------------|
|IniFile.cs | 讀寫設定檔 | ✅|
|ExcelInterops.cs | Excel 非同步存取 | ⬜ 建議|
|Settings.ini | 全域狀態儲存 | ✅|
-