PR 紀律
概述
PR 耗費團隊時間。此處的紀律是預防浪費(因意外 scope 導致的重新審查、合併到錯誤分支、錯誤的歸屬),而非事後補救。
原則: 涉及架構時必須先取得共識,始終保持一致的格式,合併目標需檢查兩次。
開始時宣告:「正在為此 PR 套用 pr-discipline。」
步驟 1:在寫程式之前(若適用)先與維護者對齊
若變更涉及以下項目,請在開 PR 之前先向模組維護者發送一段提案:
- 現有端點的回應格式。
- DTO 或驗證器的結構。
- 跨儲存庫模式(驗證器、服務、錯誤處理)。
- 架構抽象化(新的裝飾器、服務層、模組層級模式)。
範本:
「[維護者],我要為了功能 Y 做 X。範圍:a, b, c。其他 X 因 Z 原因保持不變。可以嗎?」
等待確認。若有異議,先在文字上迭代(成本低)。若獲得批准,PR 便無需事後辯論。
不適用於: 不改變合約的錯誤修正、遵循現有模式的功能、模組內的內部重構、單純的文件或測試。
步驟 2:確認 PR 的正確基礎分支
PR 的基礎分支不總是 main。呼叫 use-worktrees 技能(步驟 1 — 完整瀑布流程)來解決。一旦解決,結果會保留在自動記憶中,後續呼叫可直接使用,無需再次詢問。
此技能尊重常見的流程:
| 流程 | 功能分支從何處分支 | PR 指向何處 |
|---|---|---|
| 有暫存緩衝區的後端 | staging | staging(之後 staging → main 另外處理) |
| 行動端 / 簡易網頁 | main 或 main-web | main 或 main-web |
| 傳統 Gitflow | dev | dev(之後 dev → master) |
| Trunk-based | main 或 trunk | main 或 trunk |
不要假設;始終解決。
步驟 3:重複使用先於建立
在新增任何檔案、驗證器、守衛、服務、元件、輔助函式、常數或型別之前:呼叫 reuse-existing-patterns 技能。該技能涵蓋如何搜尋(按類別)、何時使用現有資源、以及何時可以合法建立平行版本(需在 PR 內文中附上理由)。
沒有經過這個反射動作就開始寫程式——這是審查評論中出現「這個已經存在了,請使用現有模式」的首要原因。
步驟 4:建立 PR — 標題、內文、歸屬
標題
- 少於 70 字元。較長的內容請放在內文。
- 採用儲存庫的格式(若使用 Conventional Commits 則依循;若無,則用簡短句子)。
- 使用團隊的語言(若團隊使用西班牙文,則標題使用西班牙文)。
內文
最低限度範本:
## 摘要
<1-3 個要點,說明變更內容與原因>
## 測試計畫
- [ ] <要驗證的命令或流程>
- [ ] <其他>
## 風險
<邊界情況、回滾計畫、相依性>
若儲存庫有範本(.github/PULL_REQUEST_TEMPLATE.md),則以此為基礎。
歸屬(提交身分)
絕對不要在 git commit 中傳遞 -c user.email=... 或 -c user.name=...。工作用的儲存庫已有正確的本地設定;全域設定通常是個人的。意外覆蓋會導致工作提交歸屬到個人的 GitHub 帳號。
推送前先確認:
git log -1 --format='%an <%ae>'
若電子郵件不是工作用的,請勿推送 — 先修正:
git commit --amend --reset-author # 使用目前的本地設定
請勿只為了歸屬問題而改寫 main/staging(受保護分支)的歷史——強制推送的成本超過了效益。
步驟 5:上傳帶有反引號的乾淨 Markdown
gh pr edit --body-file 和 gh pr comment --body 會跳脫反引號 為 \``,破壞格式。解決方案:使用 gh api -X PATCH` 搭配 JSON 負載。
python3 <<'PY'
import json, subprocess
with open('/tmp/body.md') as f:
body = f.read()
subprocess.run(
['gh', 'api', '-X', 'PATCH', 'repos/<org>/<repo>/pulls/<num>', '--input', '-'],
input=json.dumps({'body': body}), text=True, check=True
)
PY
確認:
gh pr view <num> --json body --jq .body | head -10
若反引號是文字形式(未被跳脫),即為乾淨。
步驟 6:審查評論的風格
- 按嚴重程度分組:阻擋性 / 次要改進 / 吹毛求疵。
- 每項發現一個要點,單行呈現:什麼 + 原因 + 建議修正。
- 使用
file.ts:42格式引用行號(GitHub 上可點選)。 - 先說明原因,再提出觀察。如同同儕的邊註,而非外部稽核。
- 若作者已在 PR 內文中說明其推理,請與該推理互動 — 不要從基本原則重新發明。
- 不要使用「B1」、「M2」這類人造標籤。若沒有幫助,不要附上範例程式碼區塊。
有經驗的人類審查者會注意到「AI 傾倒」的特徵:過於對稱的結構、相同的過渡用語、缺乏作者上下文的要點。
步驟 7:合併 — 根據團隊流程選擇 squash 或 merge
確認儲存庫的慣例。常見模式:
| 儲存庫類型 | 功能 → 整合分支 | 整合分支 → main |
|---|---|---|
| 有暫存分支的後端 | squash | merge commit |
| 無暫存分支的行動端/網頁 | squash 或 merge | N/A |
| 傳統 Gitflow | merge 到 dev | merge 到 master |
為什麼在有暫存分支的儲存庫中不對稱: 如果對 staging → main 做 squash,PR 會看起來很龐大(重複了已經 squash 過的 N 個功能)。Merge commit 則保留了功能進入 staging 時的原始清單。
# 功能 → staging
gh pr merge --squash <num>
# staging → main(在分離的儲存庫中)
git pr merge --merge <num>
如果儲存庫有暫存分支,絕對不要直接合併到 main:
git revert可以撤銷程式碼,但提交記錄會永遠留在歷史中——整個團隊都看得到。- 在按下按鈕之前,三重檢查
gh pr create的--base和 GitHub UI 中的合併目標。 - 若有疑問,在執行
gh pr merge之前先問使用者:「我要合併到 staging 嗎?」
快速參考
| 情境 | 動作 |
|---|---|
| 變更涉及合約或架構 | 步驟 1:與維護者對齊 |
| 建立分支 | 步驟 2:使用瀑布流程解決基礎分支 |
| 開始實作 | 步驟 3:搜尋儲存庫中是否已有該模式 |
| 準備開 PR | 步驟 4:標題小於 70 字、內文使用範本、驗證歸屬 |
| 內文或評論包含程式碼 | 步驟 5:使用 gh api PATCH JSON |
| 留下審查 | 步驟 6:按嚴重程度分組,先說明原因 |
| 合併 | 步驟 7:根據慣例選擇 squash 或 merge |
| 有暫存分支的儲存庫 | 絕不直接合併到 main |
常見錯誤
- 未經預先對齊就開架構性 PR。 導致 2-3 輪重新審查和事後辯論。請在寫程式之前先發送提案段落。
- 當已有等效檔案或抽象化時,仍建立新的。 先搜尋(步驟 3)。審查者會指出。
- 標題超過 70 個字元,卻把完整描述放進去。 標題要短,細節放在內文。
- 在腳本化提交時傳遞
-c user.email。 會用個人的全域設定覆蓋工作用的本地設定。 gh pr edit --body-file file.md搭配反引號。 它會跳脫。請使用gh api PATCH搭配 JSON。- 審查使用敘述性散文。 使用者想要按嚴重程度分組的要點——可掃描,不是敘事。
- squash staging → main。 重複了每個功能已經 squash 過的歷史。請使用 merge commit。
- 「因為很小」就直接合併到 main。 提交會永遠留在歷史中,所有人都看得到。
紅旗
絕對不要:
- 未經事先對齊就開涉及合約/架構的 PR。
- 當已有等效物件時,建立平行檔案或抽象化。
- 在
git commit中傳遞-c user.email/-c user.name。 - 未驗證
git log -1 --format='%ae'就推送。 - 對包含反引號的 Markdown 使用
gh pr edit --body-file。 - 在有暫存分支的儲存庫中直接合併到 main。
- squash staging → main。
- 未按嚴重程度分組就留下審查。
始終:
- 對架構性變更,預先與維護者對齊。
- 使用瀑布流程解決 PR 的基礎分支,不要假設是
main。 - 在建立新模式之前,先搜尋儲存庫中是否已存在該模式。
- 標題少於 70 字 + 內文包含摘要 + 測試計畫 + 風險。
- 推送前先驗證提交的歸屬。
- 對包含程式碼的 Markdown 使用
gh api PATCH搭配 JSON。 - 審查時按嚴重程度分組,先說明原因。
- 功能 → 整合分支使用 squash,整合分支 → main 使用 merge commit。