本文件為「設計欄位」情境的完整指引。適用於 ticket 模板、YAML frontmatter、API response、database schema、配置檔案等任何多欄位結構的設計。

為什麼獨立成篇:欄位設計的錯誤會被模板放大。一個設計不良的 ticket 模板會產生上百個混淆 ticket、上千則語意空洞的資料。欄位一旦上線就難以撤回,因為後續所有資料都已按此格式寫入。

自包含聲明:閱讀本文件不需要先讀其他 reference。核心原則的精要在本文件內展開於「欄位設計」情境;需要跨情境套用時,才去讀對應的 writing-*.md


TL;DR(30 秒版本)

  1. 每個欄位承載一個維度,不同欄位描述同一件事的不同面向(what 描述動作、why 陳述動機、acceptance 定義可驗證條件)。
  2. frontmatter 欄位為程式化查詢服務,ID 格式、enum 值、布林命名要穩定可 grep。
  3. 欄位名稱暗示其問什麼問題why 問動機,how 問策略,blockedBy 問阻塞關係)。
  4. 欄位值格式一致,enum 有限集優於自由文字,複合值用穩定分隔符。
  5. 新增欄位前問七個問題(見「新增欄位的決策框架」章節),避免欄位膨脹。

目錄

  1. 原子化 × 欄位:每個欄位承載一個維度
  2. 索引 × 欄位:程式化查詢、ID 格式、frontmatter 設計
  3. 意圖顯性 × 欄位:欄位名稱即提問
  4. 可查詢性 × 欄位:值格式一致性、enum 命名
  5. 欄位設計 meta:新增欄位的決策框架
  6. Ticket 六欄位角度解析(六欄位總表 + 詳細範例連結)
  7. 非 ticket 情境:YAML 配置、API response

1. 原子化 × 欄位:每個欄位承載一個維度

原則:每個欄位只回答一個問題。若一個欄位同時承載兩個維度,填寫者會混淆,查詢者會誤讀。

判斷標準

一個欄位的內容若出現以下徵兆,代表「承載太多維度」,應拆分:

徵兆範例拆分方式
描述需要「和」連接status: "in_progress 且 blocked by API"拆成 status + blockedBy
同一欄位混用動作與動機what: "修 bug 因為用戶回報崩潰"拆成 what(修 bug)+ why(用戶回報崩潰影響可用性)
單欄位塞多個可查詢值tags: "p0 security urgent"改為 priority: p0 + category: security + urgency: high
用自由文字藏結構資料notes: "owner=alice, due=2026-04-20"拆成 owner: alice + due: 2026-04-20

反例:一個欄位承載兩個維度

1# 錯誤:status 同時表達「進度」和「阻塞原因」
2status: "in_progress_waiting_for_api_team"

問題:

  • 查詢「所有 in_progress 的 ticket」需要字串前綴比對,不穩定。
  • 改阻塞對象時,狀態字串也要改,污染查詢歷史。
  • 報表無法分別統計「進度分佈」和「阻塞分佈」。

正確:兩個維度各自一個欄位

1status: in_progress
2blockedBy:
3  - team: api
4    reason: "等待 /v2/users 端點上線"

每個欄位只回答一個問題status 回答「目前在哪個階段」,blockedBy 回答「被什麼擋住」。

原子化測試

拿一個欄位問三個問題:

  1. 這個欄位回答什麼問題? — 若答案需要「和」連接兩個問題,必須拆分。
  2. 這個欄位的值能獨立變更嗎? — 若必須連動其他資訊,代表混在一起了。
  3. 能用此欄位單獨做統計/排序嗎? — 若不能,代表它混入了其他維度。

2. 索引 × 欄位:程式化查詢、ID 格式、frontmatter 設計

原則:frontmatter 欄位存在的理由之一是讓程式能查詢。人眼看不出結構,程式才看得出。所以 ID 格式、enum 值、布林欄位的命名要為「程式化查詢」服務。

ID 格式設計

好的 ID 同時對人和程式友善。以下是穩定 ID 格式的檢查點:

要求正確範例錯誤範例原因
分層結構可拆解v1.2.0-W03-021.4ticket_74_sub7前者可用分隔符拆成版本/wave/序號/子序號
穩定分隔符- 分層、. 分子層混用 _-程式 regex 才能一致比對
固定位數(可選)P001P002P1P10P100字典序排序才會等於數值序
不含空白與特殊字元prop-cache-cleanupprop cache cleanup!避免需要引號與跳脫
可預測的命名空間PROP- / UC- / SPEC- 前綴無前綴純數字能靠前綴快速過濾類別

frontmatter 為查詢服務

frontmatter 的目的是「讓特定查詢變快」、不是「把資訊塞進去」 — 因為光把資訊塞進去只是換個地方存、查詢仍然要 scan;要查詢變快、欄位要做特定設計(穩定鍵名、可索引的值類型、前綴命名空間)才能匹配查詢工具的索引機制。把目的搞錯會做出「資訊完整但查詢仍要全文掃」的 frontmatter、讀者得到結果但工具拿不到效益。

1---
2id: ticket-001
3title: "修復登入崩潰"
4status: in_progress
5priority: P0
6blockedBy: []
7version: v1.2.0
8wave: 3
9---

每個欄位服務一種查詢需求:

欄位服務什麼查詢
status列出所有「進行中」的項目
priority依優先順序排序
blockedBy找出被阻塞的項目
version / wave按發佈批次篩選

欄位不服務的查詢就不要存。例如「建立者的辦公座位」放在 frontmatter 只會污染欄位密度。

為「程式能看」優先,為「人能看」是加分

frontmatter 的值優先讓程式 grep/parse,再由顯示層(UI、報表)翻譯給人。

優先程式優先人
status: in_progressstatus: "進行中(已派發工程師)"
priority: P0priority: "緊急 — 阻塞發佈"
blockedBy: [ticket-042]blockedBy: "等 ticket-042 的 API 做完"

若需要給人看的補充描述,用獨立的自由文字欄位承接(如 blockedByReason),frontmatter 主欄位保持結構化。


3. 意圖顯性 × 欄位:欄位名稱即提問

原則:欄位名稱本身就是一個問題。填寫者看到欄位名,應立刻知道要填什麼答案。若欄位名需要額外文件解釋,代表命名不夠顯性。

好欄位名 = 好問題

欄位名它問的問題填寫者的思考
what「做了什麼?」描述動作/內容
why「為什麼需要?」陳述動機/業務理由
when「什麼時候觸發?」條件/時機
where「影響範圍?」檔案/模組/層級
how「怎麼做?」實作策略
acceptance「怎樣算完成?」可驗證條件
blockedBy「被什麼擋住?」依賴項目
owner「誰負責?」單一責任人
deprecatedAt「何時廢棄?」日期或版本號

反例:名稱無法暗示問題

模糊欄位名問題改善
data什麼資料?payload / userProfile / metrics 依內容
info什麼資訊?description / errorDetail / author
meta描述什麼?createdBy / source / tags
config配置什麼?retryPolicy / cacheTtl
flag什麼旗標?isPublished / hasWarning
type什麼類型?eventType / userRole / errorCategory

布林欄位用 is_ / has_ / can_ 開頭

錯誤正確原因
activeisActive看到就知道是 true/false
permissionhasPermission暗示「擁有關係」
editcanEdit暗示「能力檢查」
visibleisVisible避免與名詞混淆

欄位名稱體現抽象層

一份文件內的欄位應處於同一抽象層。混層會讓讀者不知道該看哪個。

1# 錯誤:混抽象層
2what: "修 bug"                           # 業務層
3implementationDetail: "修改 auth.py 第 42 行"  # 實作層

改善:what 留在業務層,實作細節進 howwhere.files


4. 可查詢性 × 欄位:值格式一致性、enum 命名

原則:同一個欄位的值要有穩定格式,讓 grep/filter/sort 可預測。格式不一致會讓查詢需要 N 個 regex 才能覆蓋,最終退化成「用肉眼翻」。

enum 優於自由文字

有限集合的欄位應列出所有合法值:

1# 正確:status 有固定集合
2status: in_progress    # 僅限 pending / in_progress / blocked / completed / cancelled
3
4# 錯誤:狀態用自由文字
5status: "進行中,但有點卡"

enum 的三個好處:

  1. grep 精確(status: in_progress 無歧義)
  2. 值變更能被編譯器/驗證器捕捉
  3. 統計時不用做語意聚類

enum 命名規則

要求正確錯誤
全小寫、單字用 _ 連接in_progressinProgress / In-Progress
語意中立(不帶情緒)cancelledabandoned_by_team
涵蓋周延(加 unknownother 兜底)unknown漏 fallback 導致必填卡關
避免數字後綴(除非真的有序)high / medium / lowlevel1 / level2

複合值的穩定分隔符

若欄位必須承載複合值,用穩定的分隔符讓 regex 可拆。

1# 正確:用 : 分隔方向和目標
2direction: "to-sibling:ticket-045"
3
4# 錯誤:自由文字
5direction: "指向兄弟 ticket 045"

常見分隔符慣例:

分隔符用途範例
:維度:值scope:apitype:bug
/路徑src/auth/login.py
,多值列表(若不用陣列)a11y,i18n,perf
/ ->流向pending → in_progress

禁止混用分隔符。若一個欄位用 :,整個系統都要用 :

欄位值前綴做分類

當同類型但需細分時,用固定前綴而非混在自由文字中。

1# 正確:ID 前綴暗示類別
2id: PROP-042    # proposal (portability-allow: teaching example)
3id: UC-007      # use case
4id: SPEC-012    # spec
5
6# 錯誤:自由文字描述類別
7id: "proposal 42"

日期時間統一 ISO 8601

1# 正確
2createdAt: "2026-04-16"
3startedAt: "2026-04-16T09:30:00+08:00"
4
5# 錯誤
6createdAt: "2026/4/16"       # 分隔符不穩
7createdAt: "April 16, 2026"  # 不可排序
8createdAt: "昨天"             # 不可重複解析

5. 欄位設計 meta:新增欄位的決策框架

原則:欄位一旦加入就難以撤回。新增前必問以下七個問題,避免欄位膨脹、語意重疊。

新增欄位前必問清單

#問題若答「否」的處理
1這個欄位回答什麼問題?(單一維度)若回答多個問題 → 拆成多欄位
2是否已有欄位涵蓋同一維度?若有 → 擴充既有欄位或重新命名,不新增
3至少有一個查詢情境需要它嗎?若無 → 放入自由文字 notes,不進 frontmatter
4值域是否有限可枚舉?若是 → 用 enum;若否 → 確認自由文字真的必要
5缺省值(missing)有明確語意嗎?若無 → 補上預設值或註明 nullable
6填寫者有能力正確填寫嗎?若否 → 改成程式自動填,或提供 enum 選單
7停用時如何淘汰?若無計畫 → 先標註 deprecatedAt 欄位策略

欄位語意重疊檢查

新增欄位時最常見的錯誤是「與既有欄位語意重疊」。檢查方式:

  1. 列出新欄位的提問(它要回答什麼問題)。
  2. 對照既有欄位的提問,找是否有重複。
  3. 若有重複,選其一:
    • 合併:新增的維度其實可以塞進既有欄位(若不違反原子化)。
    • 改名:讓兩個欄位的提問有明確區別。
    • 捨棄:若既有欄位已能滿足,不新增。

欄位膨脹的警訊

警訊意義行動
一個 schema 超過 15 個 frontmatter 欄位單一物件承載過多責任分拆為子物件(nested)或獨立 schema
有些欄位在 > 80% 的資料中為空該欄位的查詢需求薄弱降級為自由文字 notes
欄位的填寫規則寫在多份 wiki規則太複雜改為 enum 或引入驗證器
兩個欄位經常「一起填/一起空」語意耦合合併或用 nested 結構

6. Ticket 六欄位角度解析

ticket 模板的 what / why / when / where / how / acceptance 六欄位是刻意設計的多角度描述系統。每個欄位從不同角度描述同一個 ticket,合起來才是完整規格。

六欄位角度總表

欄位角度正確範例摘要不該寫什麼常見混淆模式檢驗標準
what做什麼"新增 2FA 設定開關,支援 TOTP 與 email 驗證碼"動機、實作、驗收用「因為…所以」開頭,把 why 塞進來讀者能立刻想像成品
why為什麼做"登入崩潰影響 8% 用戶,每週 40 張客服工單"動作、實作細節寫「要修 X bug,修改 Y 檔案」,把 what/how 塞進來含業務指標或成本數字
when何時啟動 / 何時截止"v1.2.0 發佈前;依賴 W03-021 完成後啟動"產品行為時序寫「當用戶點擊…系統會…」,描述產品行為而非 ticket 啟動條件條件可被程式或人工驗證
where影響範圍layer: Application; files: [src/auth/login_service.py, ...]抽象功能名寫「相關的所有地方」,不列具體路徑有具體檔案/模組清單
how怎麼做"加 guard clause → Result 模式 → 補 6 個失敗情境測試"驗收條件、動機寫「做完要通過所有測試,成功率 ≥ 99.5%」,把 acceptance 塞進來有步驟、順序、技術選擇
acceptance怎樣算完成"[ ] Staging 連續 24h 成功率 ≥ 99.5%"做法、動機寫「體驗變好」「品質提升」等不可量測描述每條都能被勾選(量化或證據性)

詳細範例(每個欄位各 1 個正確 + 1 個混淆共 12 項):designing-fields-ticket-6w.md


7. 非 ticket 情境:YAML 配置、API response

核心原則不只適用 ticket。以下兩個情境示範同樣的思維。

7.1 YAML 配置檔案

原則應用

原則在配置檔案的具體形式
原子化每個配置鍵承載一個決定(超時、重試、快取各自獨立)
索引用 namespace 分組(database.pool.size 而非 dbPoolSize
意圖顯性retryMaxAttempts 優於 retryattempts
可查詢性布林用 isEnabled / hasFallback;時長用 timeoutSeconds 後綴
欄位設計新增配置前問「這個值會隨環境變嗎?(是→配置;否→常數)」

範例(正確)

 1database:
 2  host: "db.internal"
 3  port: 5432
 4  pool:
 5    minSize: 5
 6    maxSize: 50
 7    acquireTimeoutSeconds: 10
 8
 9retry:
10  maxAttempts: 3
11  initialDelayMs: 100
12  backoffMultiplier: 2
13  retryableErrors:
14    - connection_timeout
15    - transient_network_error

範例(常見混淆)

1# 錯誤:單位混亂、命名模糊、enum 變自由文字
2db:
3  timeout: 10          # 秒?毫秒?
4  retry: true          # 布林還是次數?
5  errors: "timeout,network"  # 為何不用列表?
6  mode: "production, maybe"  # enum 還是形容詞?

混淆分析

  • timeout 沒標單位 → 應為 timeoutSecondstimeoutMs
  • retry: true 語意不清 → 改為 retry.isEnabled + retry.maxAttempts
  • errors 用字串列表 → 應為 YAML 陣列以利 parse
  • mode 含「maybe」 → enum 必須是有限集合

7.2 API Response Schema

原則應用

原則在 API response 的具體形式
原子化資料欄位與 meta 欄位分開(資料放 data,狀態放 status
索引穩定 ID 欄位(idcursor)讓分頁與查詢可預測
意圖顯性isPaginatedhasMoretotalCount 各司其職
可查詢性錯誤碼用 enum(errorCode: "INVALID_TOKEN")而非自由文字
欄位設計新增欄位前問「client 真的會用嗎?還是只是 server 方便?」

範例(正確)

 1{
 2  "status": "success",
 3  "data": {
 4    "users": [
 5      { "id": "usr_001", "email": "alice@example.com", "role": "admin" }
 6    ]
 7  },
 8  "pagination": {
 9    "hasMore": true,
10    "nextCursor": "eyJpZCI6InVzcl8wMDEifQ==",
11    "totalCount": 1247
12  },
13  "meta": {
14    "requestId": "req_abc123",
15    "serverTime": "2026-04-16T09:30:00Z"
16  }
17}

範例(常見混淆)

1{
2  "ok": 1,
3  "data": {
4    "result": "...",
5    "error": null,
6    "nextPage": "yes"
7  },
8  "info": "取得 10 筆資料,還有更多"
9}

混淆分析

  • ok: 1 數字當布林 → 應為 status: "success" enum
  • data 同時放業務資料與錯誤(result + error)→ 原子化違反,應分開
  • nextPage: "yes" 字串當布林 → 應為 hasMore: true
  • info 為自由文字夾帶結構資訊 → 拆成 totalCount: 10 + hasMore: true

可攜性自檢

本文件可獨立閱讀,不引用任何特定專案的路徑、ticket ID、commit hash 或 hook 系統。核心原則與 ticket 六欄位範例皆為通用概念,可套用於任何採用多欄位結構的文件系統。


速查表

想做的事看哪一節
判斷欄位是否承載太多維度第 1 節「原子化測試」
設計 ID 格式第 2 節「ID 格式設計」
讓欄位名自我說明第 3 節「好欄位名 = 好問題」
enum 命名規則第 4 節「enum 命名規則」
新增欄位前的檢核第 5 節「新增欄位前必問清單」
ticket what vs why 混淆第 6 節「六欄位角度總表」+ designing-fields-ticket-6w.md what/why 章節
可驗證的 acceptance 寫法第 6 節「六欄位角度總表」+ designing-fields-ticket-6w.md acceptance 章節
YAML 配置命名第 7.1
API response 欄位設計第 7.2