設計(canon 規範)
設計應確保後續的工作階段無需詢問任何人即可理解為什麼(決策,canon/decisions/D-n.md)以及必須滿足什麼(規則,canon/rules/<domain>/BR-n.md)。僅存在於本次對話中的設計將會遺失。
1. 選擇模式
| 模式 | 閱讀 | 重點 |
|---|---|---|
architecture | references/mode-architecture.md, references/architecture-patterns.md | 層次、邊界、C4、遷移 |
api | references/mode-api.md, references/api-design.md | 端點 / 命令、錯誤、認證、分頁 |
database | references/mode-database.md, references/database-design.md | 結構描述、索引、遷移、存取模式 |
system | references/mode-system.md, references/system-design.md | SLA、故障模式、容量 |
module | references/mode-module.md | 職責、深度(刪除測試)、接縫、依賴分類 |
僅閱讀您所需模式的文件。當設計觸及跨領域問題時,才閱讀跨領域參考文件(references/cross-cutting/:授權、錯誤處理、冪等性、工作階段識別)。
2. 設計之前
canon context <path|domain|C-n>:取得整個當前領域、其規則、已接受的決策以及未完成的變更。設計不得與現行規則或已接受的決策相牴觸;若必須如此,則應在相同變更中說明並修改它們。canon search <topic>:搜尋關於同一問題的先前決策與備註。應擴展或取代(--supersedes D-n),切勿無聲地重做。- 在自行發明之前,先查看 1–3 個成熟產品或函式庫如何解決相同問題。
- 在提出結構之前,先閱讀您要修改的現有程式碼(符號、呼叫者)。
3. 設計
- 在列出選項之前先界定問題:此設計必須回答哪些問題?必須服務哪些情境?在評估現有設計時,其缺陷清單是輸入項,而非架構本身:僅根據缺陷清單制定的計畫是修補,而非設計。
- 提供至少兩個真實選項及其取捨,並提出建議。真實意味著不同的設計,而非同一設計的不同程度(例如「修正 3 件事」與「修正 10 件事」是同一選項)。三個或更多選項,或多個維度 → 應逐維度撰寫推理過程,而非憑記憶列出優缺點。
- 在給出建議之前,讓一個不知您建議的子代理挑戰一次。詢問它:問題框架本身是否錯誤?是否遺漏了真正不同的設計?每個選項在何種情境下會失敗?處理完它提出的問題後,再提出建議。僅在模組內的小型設計(不改變任何介面)時可跳過此步驟,並說明跳過原因。
- 在提議任何函式庫、版本或 API 之前,先查閱最新的官方文件。
- 對於一個透過可捨棄原型比辯論更快速解答的問題(例如此狀態機或資料模型在實際使用中是否成立?幾個畫面佈局中哪個有效?),應先使用 canons-prototype 技能解決;設計隨後引用原型所顯示的結果。
- 先量化模糊的目標(例如「快」→ 在 Y rps 下 p99 低於 X 毫秒)。
- 設計服務的每個情境都應至少指定一個它必須處理的業務級故障(例如達到限制、付款失敗、流程中權限被撤銷、上游中斷),以及它如何大聲且可恢復地失敗;「無故障模式適用」必須經過論證,而非假設。
- 將每個選項的複雜度與其在情境發生時所付出的代價進行權衡,並向每個情境提出一個問題:正常操作會導致它發生嗎?對於 canon 而言,多個工作階段同時工作並完成是正常使用;測試如何重現它並非決定因素。需要異常設定或操作的情境(例如使用者對系統命令的包裝、無人使用的非預設後端、在步驟中殺死某個特定行程、未跨越安全邊界的惡意輸入)應成為已知限制(決策的「後果」或規則中的一行),而非新的機制;資料遺失、安全性以及違反專案的承諾(對 canon 而言,即 canon/product.md 中的 P1 至 P7)是例外情況。
- 自行根據模式的視角審查草稿。平行審查子代理(每個視角一個,請參閱模式檔案)是可選的:每個子代理從零上下文開始,會耗費數萬個 Token,因此僅在影響範圍很大時(跨模組、公開 API、資料遷移、安全性)才使用它們。
質量門檻(所有項目都必須通過,否則需說明哪一項未通過及其原因)
| 模式 | 門檻 |
|---|---|
architecture | 每個非功能性需求都有可衡量的架構答案;每個張力都有附帶理由的決策;高風險有緩解措施;遷移階段可回滾 |
api | 每個操作都有其錯誤;每個操作都有其認證要求;安全問題已解決或已附帶理由接受;已說明分頁和速率限制 |
database | 所有外部鍵都已建立索引;每個資料表都由一個存取模式證明其合理性;每個結構描述變更都有零停機遷移;金錢不使用浮點數,時間戳記包含時區 |
system | SLA 目標(吞吐量、p99、可用性);沒有未經緩解的單點故障;每個元件都有故障模式和降級方式;容量為 1×/10×/100× |
module | 沒有依賴循環;每個模組有單一明確的職責;每個模組通過刪除測試;沒有僅有一個適配器的接縫;公開表面足夠以黑盒方式進行測試;依賴項可注入 |
跨領域(每次都要評估;僅在有明確理由時才跳過)
| 關注點 | 時機 | 做法 |
|---|---|---|
| 安全性 | 使用者輸入、認證、儲存、外部呼叫 | 追蹤資料流,列出威脅與緩解措施 |
| 並行性 | 共享狀態、多個工作階段或行程 | 指出在正常使用中可能導致問題的交錯情況,以及使其具原子性的機制;較罕見的情況為已知限制,除非導致資料遺失 |
| 效能 | 延遲敏感路徑、大量資料 | 設定預算,並證明設計能滿足預算 |
| 可觀測性 | 新元件 | 記錄或記錄了什麼,以及如何發現故障 |
| 資料隱私 | PII 或其他使用者資料 | 分類其敏感性;傳輸中和靜態加密;保留期限和刪除;誰讀取或更改的稽核軌跡 |
4. 記錄設計
- 決策:針對每個真實選擇:
canon decision new --title "..."→ 填寫「背景」(事實、資料)、「方案」(每個選項標記為「採用」或「否決」並附帶原因,至少各一個)、「決定」、「後果」。使用中文書寫(canon/ 文件為中文)。在選項下方引用事實來源,每項一行:- 來源:<URL 或儲存庫路徑> "<精確引用,8 個以上字元>"— 先研究外部實踐,然後引用;新決策至少需要一個來源,而落地時會擷取每個新來源,除非引文確實存在,否則拒絕。不要添加自我評估的分數或信心水準:無人能驗證它們。 - 規則:針對設計所承諾產品的每個行為:
canon rule new --domain <d> --title "..."。一條規則 = 一個觸發條件和一個結果;填寫「規則」和「例子」,並提供邊界兩側的具體輸入。此變更不實作的行為(其程式碼在計畫的後續步驟中)在規則的前言中獲得status: planned:它需要「規則」和「例子」,但無需測試,而實作該行為的變更會將其列在rules:下並使其生效。在計畫規則的「例子」中,僅固定設計所確定的邊界;實作時發現的邊際情況則由實作該步驟的變更添加。 - 變更檔案(
canon/changes/C-n.md):「為什麼」/「要改什麼」,並引用決策和規則。 - 如果設計變更了容器(執行時部分、其資料所在位置)或主要流程,請在相同變更中更新
canon/architecture.md:它描述系統的當前狀態,每個部分以其決策編號結尾。檢查其決策編號與您剛做出的決策:被取代或修改的決策意味著需要重寫一行。 - 照常落地(
canon work land)。規則審查會依據整個領域和概覽來閱讀決策和規則;修正它阻止的問題。
沒有針對每個設計的獨立文件:值得在實作前修正的介面(型別、命令簽名、結構描述)應寫入變更檔案或程式碼本身。