產品開發 Workflow
從「使用者需求」到「Code」的五階段流程。 核心原則:前段守住人類判斷,後段才把實作交給 AI。
流程總覽
flowchart LR R[User Requirement<br/>使用者需求] --> F[ Functional Spec<br/>功能規格] F --> O[ OOA<br/>物件導向分析] O --> P[Pseudo Code<br/>虛擬碼] P --> C[Code<br/>程式碼] classDef human fill:#e8f0ff,stroke:#3b6,stroke-width:2px classDef hybrid fill:#fff4e0,stroke:#e90,stroke-width:2px classDef ai fill:#ffe8e8,stroke:#c33,stroke-width:2px class R,F human class O,P hybrid class C ai
| 顏色 | 意義 |
|---|---|
| 藍 | 人主導,AI 協助 |
| 黃 | 協作(共同決定) |
| 紅 | AI 主導,人 review |
階段一:User Requirement(使用者需求)
性質
紀錄區,不是規格。只負責收集與保存原始需求素材,不要求結構化。
內容形式(任何皆可)
- 文字筆記、會議紀錄、訪談逐字稿
- 圖片、白板照片、手繪草圖
- 語音檔、影片片段
- 既有文件(PDF、Email、Slack 對話截圖)
- 連結(Jira、Linear、Notion)
唯一硬性要求
來源可追溯。每一份素材都要能說出「誰、何時、為何提的」。
產出結構
requirement/{feature}/
├── index.md ← 索引 + 一句話 problem statement
├── raw/ ← 原始素材(圖、錄音、檔案)
└── notes/ ← 整理過的筆記(可選)
index.md 至少包含:
- Problem statement:一句話講清楚要解決什麼問題
- Stakeholders:誰提的、誰受影響
- 素材清單:每筆有來源、日期、簡述
- 未解問題:還沒釐清的點(給下一階段消化)
- 需求收斂結論:把紀錄收斂成 Stage 2 的乾淨 input;每點回指素材/已答未解問題,不寫 scope(做不做什麼是 Stage 2)
AI vs 人
| 動作 | 主導 |
|---|---|
| 收集素材 | 人 |
| 整理 index、打標籤 | AI |
| 標記重複/矛盾 | AI |
| 對未解問題提出澄清題目 | AI |
| 草擬需求收斂結論 | AI(人確認) |
| 確認素材真實性與優先級 | 人 |
Exit Gate(離開本階段的條件)
index.md存在- Problem statement 一句話寫得出來
- 至少有一份原始素材或筆記
- AI 已挑出未解問題清單,人確認可以進下一階段
- 需求收斂結論已寫,且每點可回指素材/已答未解問題、無 scope 決策
階段二:Functional Spec(功能規格)
性質
結構化的功能定義。比 Story 更明確,比軟體規格(OOA、API spec)更高層。 回答「這個功能要做什麼、長什麼樣、邊界在哪」,但不回答「怎麼實作」。
必備內容
- Use Cases
- 角色(Actor)+ 觸發條件 + 行為 + 預期結果
- 至少涵蓋 happy path + 主要 alternate path
- 流程圖或狀態機(Mermaid 即可)
- 使用者操作流程,或系統狀態轉換
- 輸入 / 輸出範例
- 具體值,不要只寫型別
- 包含正常值 + 邊界值 + 異常值
- 邊界條件 / 異常情境
- 列舉式:什麼情況下應該發生什麼
可選但建議
- 驗證 Python script
- 把核心邏輯寫成短腳本跑一次
- 用途:自我檢核 spec 是否自洽、給人 demo 用
- 不強制 —— 純 CRUD 或 UI 類功能寫了反而冗餘
禁區(這些是 OOA 的事,不要在這寫)
- Class diagram、繼承關係
- DB schema、欄位定義
- API endpoint 細節(URL、HTTP method、payload)
- 套件結構、模組切分
產出
spec/{feature}/{feature}.fp.md
與 requirement/{feature}/ 同名成組。
文件頂部 breadcrumb:
> 來源:[requirement/{feature}/](../../requirement/{feature}/index.md)
> 下游:[OOA]({feature}.ooa.md)(尚未建立)AI vs 人
| 動作 | 主導 |
|---|---|
| 從 requirement 草擬 Use Case | AI |
| 補流程圖、輸入輸出範例 | AI |
| 挑漏洞(邊界、矛盾、未覆蓋的 case) | AI |
| 範圍決定(什麼做、什麼不做) | 人 |
| 優先級 | 人 |
| 最終定稿 | 人 |
Exit Gate
- 涵蓋 requirement 中所有列入範圍的 user need(逐項對照)
- Use Cases 完整(happy + 主要 alternate)
- 至少一張流程圖 / 狀態圖
- 輸入輸出範例具體(有實際值)
- 人簽核(在文件底部留簽名 + 日期)
階段三:OOA(物件導向分析)
性質
軟體規格的結構面。把 Functional Spec 翻譯成軟體世界的語彙。
內容
- 領域語彙(Ubiquitous Language)
- Object Model(類別、屬性、方法)
- 關係(繼承、組合、聚合、關聯)
- Event(領域事件)
- Use Case Sequence Diagram
產出
spec/{feature}/{feature}.ooa.md
AI vs 人
協作。AI 用結構化方法協助分析(類別圖、SOLID 檢視),人決定領域邊界與命名。
Exit Gate
- 類別圖完整(涵蓋 Functional Spec 中所有 Use Case 的物件)
- 通過 SOLID 檢視
- 領域語彙表完整
- 人簽核
階段四:Pseudo Code(虛擬碼)
性質
軟體規格的流程面。把 Functional Spec 定義的行為,展開成帶決策邏輯的流程,並用 OOA 的類別/方法結構承載。
必備
- 帶 §編號 的決策節點(後續 Code 要在註解引用)
- §編號永久:新內容附加新號、絕不重編;被取代的段落標
刪除線+ 後繼 § 指標,集中移至文件底部「§廢棄歷史」,主流程僅留一行 tombstone(去發散、保留追溯) - 對外節掛全域碼:被下游(OOA/Code/別份 spec)引用的 §-section,其權威引用形式 = 全域碼
§{SHORT}-PC-{NUMBER}(見 §可追溯性「全域編碼」);純內部子節點維持本地 §N.M - 涵蓋所有 business decision branch
- 不要寫得像真實程式語言,重點是邏輯流不是語法
產出
spec/{feature}/{feature}.pseudo.md
AI vs 人
協作。AI 起草,列出所有分支;人檢查流程正確性。
Exit Gate
- 所有 business decision 都有 §編號
- 對照 Functional Spec 的 Use Cases,每條 happy/alternate path 都覆蓋
- 人簽核
階段五:Code
性質
實作。
規範
- Code 必須在 Javadoc / 註解中引用對應虛擬碼的全域碼
§{SHORT}-PC-{NUMBER}(見 §可追溯性「全域編碼」);對外公開類別/方法另掛自身§{SHORT}-RC-{NUMBER} - 測試覆蓋 Functional Spec 中的輸入輸出範例
AI vs 人
AI 主導。人 review + 跑測試。
Exit Gate
- 測試通過(含 spec 中的範例值)
- § 引用完整
- Code review 通過
可追溯性(Traceability)
同名成組
requirement/login/ ← 階段一
spec/login/login.fp.md ← 階段二
spec/login/login.ooa.md ← 階段三
spec/login/login.pseudo.md ← 階段四
src/.../Login*.java ← 階段五
共用 / 跨 feature spec(例外)
有些 functional spec 不屬於單一 feature,而是多個 feature 共用的領域物件(Published Language),例如 spec/domain-model/domain-model.fp.md。這類 spec:
- 無 requirement 上游 —— 從多個消費 feature 的共同需求抽出,非由單一
requirement/{feature}/衍生;不適用「同名成組」回指 requirement,也不與 requirement 做上游 audit。 - 可追溯性改走 consumer → shared:各消費 feature 在自己的 spec 以 breadcrumb/相對 markdown 連結指向共用 spec(
[domain-model](../domain-model/domain-model.fp.md)),「誰引用我」由 backlinks 彙整(Quartz 站對標準 markdown 連結自動建)、不手維護。一律純 GFM、不用 wikilink(見 §連結慣例(GFM))。 - 欄位級真相在自己的 OOA:Stage 2 只放概念層;欄位/型別落在
spec/{shared}/{shared}.ooa.md。
Breadcrumb
每份文件頂部標註上游與下游連結。中斷的環節(例如某個 requirement 改了但 spec 還沒同步)一目了然。
需求收斂結論作為上游錨點
Stage 1 index.md 的「需求收斂結論」是下游 Spec 回指源頭需求的錨點:它把多輪澄清後「需求收斂到什麼」描述成 Stage 2 的乾淨 input,並讓「是哪個需求造就了這個 Spec」可被直接定位。它收斂紀錄、不下 scope —— 每點回指素材或已答未解問題,scope 仍由 Stage 2 拍板。
連結慣例(GFM)
本 repo 同時是 git 版控庫、VSCode 編輯對象,並由 Quartz v5 靜態站對外呈現。原始碼一律純 GFM(GitHub Flavored Markdown)——VSCode 預覽 / GitHub / Quartz 三處渲染一致。不用 Obsidian 方言([[wikilink]]、^block-id、非 GFM callout、#N 假 tag)。
| 連結類型 | 語法 |
|---|---|
| 檔案間 / breadcrumb | 相對路徑 markdown 連結 [顯示文字](../path/file.md)。深度:requirement/{feature}/index.md 兩層深用 ../../{stage}/…;spec/{feature}/ 內 fp/ooa/pseudo 兩層深、連 requirement 用 ../../requirement/…;同 feature 各階段為同層 sibling,直接用檔名 {feature}.ooa.md。連同資料夾 sibling 也用 markdown 連結、絕不用 wikilink |
| 同檔章節跳轉 | markdown 連結到標題錨 [顯示文字](#slug)(slug 見下「錨點 slug」) |
| 跨檔指某區段 | markdown 連結到該檔 + 標題錨 [#10](../../requirement/{feature}/index.md#未解問題) |
| 提示框 | GFM alert:> [!NOTE]/[!TIP]/[!IMPORTANT]/[!WARNING]/[!CAUTION](僅這 5 種,三處皆渲染) |
| 流程圖 | ```mermaid fenced block(GitHub / VSCode / Quartz 皆渲染) |
| 狀態標籤 | #未答、#粗排 等為純文字標記(非可點 tag、非連結);編號導航一律用標題/區段連結 |
逐項精準錨由 build 注入(不靠 Obsidian ^id):§碼(§{SHORT}-{TYPE}-{N})與未解問題逐題(#qN,依題號)由站台 pipeline 的 linkify-codes.mjs 在 build 時注入 id,原始碼維持純 GFM。引用即寫 [#N](#qN)/[§…](…#gc-…),定義處不需手寫錨。
錨點 slug(跨檔/同檔指標題):用 github-slugger 形式(小寫、空白→-、去標點,CJK 保留;如 I/O 範例→#io-範例、圖 2:…(9 站)→#圖-2…9-站)。純 CJK 無空白標點的標題 slug = 原文(如 #未解問題)。lint-links.mjs 會驗同檔錨對得上標題。
為何純 GFM:同一份原始碼同時要滿足 Obsidian + GitHub + Quartz 三套語意,是 broken link/錨點/預覽問題的根源。收斂成 GFM 一套後三處渲染一致;豐富功能(graph/backlinks/搜尋)由 Quartz 站對標準 markdown 連結提供,不靠 Obsidian。
lint-links.mjs守門擋住 wikilink/^id/非 GFM callout 回潮。
全域編碼(§{SHORT}-{TYPE}-{NUMBER})
每階段的「對外」項目散落在各自的本地編號(FS 的 B1…、Requirement 的 ^q1…、Pseudo Code 的 §1.1…)。這些號在單一文件內唯一,跨 feature/vault/repo 卻撞號(RCS 的 §1.1 與 capacity-module 的 §1.1 無法區分),一旦被外部 code repo 或別份 spec 引用就斷追溯。全域碼給「對外」項目一個跨界唯一識別。
| 段 | 說明 |
|---|---|
| 格式 | §{SPEC_SHORT_NAME}-{TYPE}-{NUMBER},例 §RCS-FS-6、§RCS-PC-7.3.6、§DM-OA-1 |
SPEC_SHORT_NAME | 每個 spec/feature 的短名,配發於 spec-codes.md(單一真相) |
TYPE | UR/FS/OA/PC/RC(對應五階段) |
NUMBER | 限 [0-9.]+:沿用本地號的數字部分,去字母前綴、保留小數階層、不零填補(B6→6、q3→3、§7.3.6→7.3.6) |
哪些算「對外」:UR=需求收斂結論;FS=邊界條件/異常情境;OA=Domain Service/Calculator/Strategy 介面/Exception 等公開型別;PC=被下游引用的 §-section(公開方法契約);RC=對外公開類別/方法。
兩層並存:全域碼 = 跨界「對外」錨點;本地 §N.M 子節點 = 文件內部導航,維持不動。只有對外項目掛全域碼。
rename ≠ renumber:「沿用本地號」⇒ 全域碼是本地號的跨界書寫形式,號碼 1:1 保留,不違反「§編號永久」。append-only、廢棄保 tombstone 同本地號紀律。
跨界即反向索引:全域碼全域唯一 ⇒ 直接 grep §{SHORT}-{TYPE}-{NUMBER}(跨 vault + code repo)即可定位所有引用,不另維護對照表。
反向追蹤
Code 註解的全域碼 §{SHORT}-RC/PC-… → 找到虛擬碼章節 → breadcrumb 往上找 OOA → spec → requirement。
出了 bug 或要改需求時,從任何一層都能定位;全域碼讓「哪份 spec 的哪一項」無歧義。
AI vs 人 邊界總覽
| 階段 | 主導 | AI 角色 | 人角色 |
|---|---|---|---|
| 1. Requirement | 人 | 整理索引、提澄清題 | 收集素材、確認真實性 |
| 2. Functional Spec | 人 | 起草、挑漏洞、補邊界 | 範圍、優先級、定稿 |
| 3. OOA | 協作 | 建模、SOLID 檢視 | 領域邊界、命名 |
| 4. Pseudo Code | 協作 | 起草、列分支 | 流程正確性 |
| 5. Code | AI | 依規格產 code | review、跑測試 |
| — 跨階段彙整 | AI | 掃描各 feature 既有狀態欄位、重生進度看板(dashboard.md) | 手寫區備註、看板上的取捨判斷 |
跨階段彙整由
spec-dashboardskill 執行(見 §跨階段進度彙整)。它是衍生視圖:不裁定、不改任何來源檔,看板每一格都回指既有欄位。
常見陷阱
- Requirement 階段就開始談解法 —— 拉回來,這階段只記「想解決什麼」並把它收斂成需求收斂結論(每點回指來源),但不下 scope 結論(做不做什麼是 Stage 2)。
- Functional Spec 寫成 API 文件 —— 砍掉,那是 OOA / Software Spec 的事。
- 跳過 Functional Spec 直接 OOA —— OOA 沒有結構化的 input 就會亂、就會發散,這正是這個 workflow 要解決的問題。
- Code 完成後文件不同步 —— 任何改動都要從對應階段往上更新,否則可追溯性瓦解。
- AI 在前段越權 —— Requirement / Functional Spec 是人決定。AI 給草稿沒問題,但拍板權在人。
跨階段一致性檢視(Consistency Audit)
解決的問題:熟悉系統的人為了效率,常會直接編輯 Functional Spec / OOA / Pseudo Code,而不從 Requirement 重新跑下來。這是合法路徑,但會產生跨階段隱性 drift —— 這個機制把 drift 顯性化。
兩條合法的變更路徑
A. 正規變更(top-down) 需求真的變了 → 從階段一開始改,往下游 cascade,每層重過 Exit Gate。
B. 直接編輯(mid-stage edit) 熟悉系統的人直接動下游文件。前置條件:改動必須符合該階段的格式規範(格式壞掉 = 自己變亂源)。動作:改完在簽核 / commit 前跑一次 Consistency Audit。
各階段直接編輯的審視範圍
修改了 Functional Spec
上游(往 Requirement 看)
- 改動是否仍在 Problem Statement 與需求收斂結論範圍內?
- 是否引入了 Requirement 沒記錄的 user need?
- 是否與既有 Requirement 筆記矛盾?
- 例外 —— 共用/跨 feature spec(如
domain-model,設計上無 requirement 上游,見 §可追溯性 → 共用/跨 feature spec):略過上述 requirement 比對,改檢查「是否引入任何消費方 spec 都未涵蓋的概念」。
下游(往 OOA / Pseudo / Code 看)
- 哪些 Use Case 被改 → 對應的 Object Model 元素?
- 哪些 I/O 範例被改 → 對應的 § 編號需重看?
- 是否新增了下游沒覆蓋的分支?
修改了 OOA
上游:Model 是否仍能服務 Functional Spec 的所有 Use Case?是否引入了 Spec 沒提到的概念? 下游:哪些 § 引用了被改動的 class / method / event?哪些 code 引用了這些 §?
修改了 Pseudo Code
上游:流程是否仍對應 Functional Spec 的 Use Cases?分支是否完整? 下游:哪些 code 在註解引用了被改的 §?需重 review + 測試重跑。
Impact Report 範本
# Consistency Audit: {feature} / {modified-stage}
- 修改文件:spec/login/login.fp.md
- 修改日期:2026-06-01
- 編輯者:Alan
- Diff 摘要:(一句話)
---
## 上游一致性
| 項目 | 狀態 | 說明 |
|---|---|---|
| Problem Statement 範圍 | ✅ / ⚠️ / ❌ | ... |
| Requirement 素材覆蓋 | ... | ... |
**需討論項目**
1. ...
## 下游影響
| 下游文件 | 受影響章節 | 行動 |
|---|---|---|
| spec/login/login.ooa.md | §3 User class | 需更新 |
| spec/login/login.pseudo.md | §2.1 驗證流程 | 需重看 |
| LoginService.java | line 42-78 | 測試重跑 |
## 建議行動清單
- [ ] 與 stakeholder X 確認 Problem Statement 是否擴張
- [ ] 更新 spec/login/login.ooa.md §3
- [ ] ...
## 簽核
- [ ] 編輯者已跑過 audit
- [ ] AI 產出 Impact Report
- [ ] Reviewer 確認:____狀態判定
| 狀態 | 意義 | 處理 |
|---|---|---|
| ✅ 一致 | 改動與上下游自洽 | 直接通過 |
| ⚠️ 需討論 | 不矛盾但可能有 implication | 進討論清單,由人裁定 |
| ❌ 矛盾 | 直接違反上游或讓下游失效 | 阻擋簽核,必須處理 |
AI vs 人
| 動作 | 主導 |
|---|---|
| 偵測 diff(git diff 或前後文比對) | AI |
| 比對上下游內容 | AI |
| 初判一致 / 需討論 / 矛盾 | AI |
| 產出 Impact Report | AI |
| 裁定 ⚠️ 項目 | 人 |
| 決定下游怎麼修(要不要修、誰修、何時修) | 人 |
| 執行下游更新 | 依該階段原本的 AI/人 邊界 |
設計理由
- AI 不自動 cascade 修下游。下游本來就有自己的 AI/人 邊界,自動 cascade 會跳過該層人判斷 —— 那就是 AI 越權。
- AI 只負責「指出影響」。修不修、怎麼修,回到該階段的規則。
- ❌ 矛盾要擋簽核。⚠️ 與 ✅ 不擋,因為熟手判斷 > 死板規則。
- 編輯者自己跑 audit。不是別人來抓 —— 是自己對自己的紀律工具。
跨階段進度彙整(Spec Dashboard)
解決的問題:多個 feature 同時在不同階段推進時,狀態散落在各文件 breadcrumb 的「狀態」欄、requirement 的「未解問題/仍待 Stage X 拍板」、breadcrumb 的「(尚未建立)」、
spec-codes.md、audit/的 ❌ —— 沒有一處能一眼看出「誰卡在第幾階段、下一步缺什麼」。
由 spec-dashboard skill 掃描上述既有欄位,彙整成根目錄 dashboard.md。它與 Consistency Audit 是不同職責:
| Consistency Audit | Spec Dashboard | |
|---|---|---|
| 觸發 | mid-stage edit 後、簽核前 | 想看/刷新整體進度時 |
| 範圍 | 單一改動的跨階段上下游 | 全 feature 的階段彙整 |
| 產出 | audit/{feature}-{date}-{stage}.md | dashboard.md |
| 判斷 | 初判 ✅/⚠️/❌ 一致性 | 不判一致性,只彙整既有狀態 |
dashboard.md 結構:自動區(階段總覽表 + 擷取式 todo,skill 全量重生)+ 手寫區(人手寫備註,skill 原樣保留、絕不覆寫)。
AI vs 人
| 動作 | 主導 |
|---|---|
| 掃描來源檔、判階段、擷取 todo、重生自動區 | AI |
| 手寫區備註、看板上的取捨判斷 | 人 |
設計原則:dashboard 是衍生視圖,不是第二真相源。自動區每一格都回指既有欄位;skill 不裁定、不改任何來源 spec、不 cascade。讀不到某欄位就標 ? 交回人,不編造。
變更管理速查
| 場景 | 路徑 | 必跑 Audit? |
|---|---|---|
| 需求真的變了 | A. top-down cascade | 否(本來就逐層改) |
| 熟手直接編輯下游文件 | B. mid-stage edit + Audit | 是 |
| 只改 Code 不改文件 | 禁止 | — |
| Code refactor 不變行為 | 允許,但 commit message 要標註,且 § 註解仍要對得上 | 否 |
核心紅線:任何路徑都不允許跳過文件直接動 Code。一旦發生,這個 feature 的可追溯性就壞了。