Development Workflow支線 B

規則怎麼死?沒死就有用?

用數個月的證據,來算算規則檔的增減帳

前言

用 AI 開發勢必會需要有規則檔。
以 Claude Code 為例,舉凡 CLAUDE.md、agent 定義、.claude/ 底下的 .md,
能為 AI 提供知識的,本文統稱規則檔;而 skill 就照叫 skill。

但規則並不是多就有用,品質才是關鍵。
尤其是大語言模型,任何事情皆是機率。
在沒維護的情況下,隨著時間前後新增的規則完全相悖,這種情況比比皆是,我們也遇到過。
叫它可以讀,又叫它不能讀,換作是人都會瘋掉,但咱們的 AI 可是會默默的自己判斷任務目標,把矛盾揉掉,完成後保持不發一語。
這可是損害可大可小的不定時機率炸彈,而且是我們的真實案例。

雖說模型持續進步,或許會越來越能自己消化這些矛盾,但消化本身就是推理,無謂的規則、多餘的推理,那消耗可都是真金白銀。
因此本文列出數個月下來我們注意到了什麼,並為此改了規則檔的哪裡。

先來個數字開場。
在這幾個月裡,光論 skill 前後出現過 41 份,巔峰期同時養著 26 份,今天剩 4 份。
規則檔和 skill 合計被刪掉的至少 47 份,它們生前都掛在真實的開發流上,譬如:

我是個潔癖的人,三不五時隨著模型進步會再派遣 AI 審視並清理邏輯相悖或毫無用處的規則,
起初是人工判斷,後續是 retro 流程來考究。

這 47 份檔案被刪的時候,commit message 都有當初理由,讓現在的我有辦法成為考古學家。
這裡挑出四種查得到死因的情況。
未來在維護自己的規則檔時,可以直接少寫這四種。

什麼死了

1. 住錯層的規則

發生啥事

專案剛起步,為了讓每次 AI 派工不要從零探索,先把現況寫進全域 CLAUDE.md 讓 AI 快速汲取:

i18n: early development phase — default to hardcoded English, use translation keys only if component already has i18n setup.

i18n 功能先 hardcode,這是其中一條,
其他還包括但不限於整棵目錄樹、一排版本號:React 19、TypeScript 5.x、MUI v7。
全是描述「前端現在長什麼樣」。

轉捩點

幾個月後專案接上 i18n,這行變成錯的,但它不會自己消失,每個讀到的 AI 都照做。
目錄樹和版本號同理:現況的規則只要程式一動就失效,而改程式的人不會想到要回頭改它,甚至不知道它的存在。
再加上我們是 monorepo,前端、後端、邊緣端全在同一棵樹下,一個純屬前端的告知在後端開發時也被「順便」讀到。

後端在開發時讀到了前端的資訊,增加了無謂的雜訊。
前端在開發時讀到了過期的資訊,增加了無謂的推理。

這些資訊住在全域,離會讓它們失效的程式太遠。
改程式的人看不到它,所以它過期,甚至因為怕影響到其他人因此選擇不改;
不相干的端卻讀得到它,所以它暗自揉合。
於是我們採用了知識分層,各端自行維護,目錄結構、測試指令、coding style 搬進各專案自己的 CLAUDE.md,讓現況住在會改它的程式旁邊。

效果

每端不用再管變更 CLAUDE.md 會不會影響其他專案,因為系統已經分層好,專注自己即可。
知識分層讓每次要讀的量縮減。今天 repo 裡的所有 CLAUDE.md 加起來有 765 行,按以前就是一股腦兒塞進去無腦讀。
但 root 那份 30 行,前端自己的 30 行,這兩份合計只有 60 行,其他規則再按任務需要讀取。

2. 寫死外部事實的

發生啥事

第一代 skill 的開頭直接指定模型:model: claude-opus-4-6
當時的想法是我要跑最強的模型,不能靠預設,寫死一個名字最保險。

轉捩點

模型升級,改成 claude-opus-4-7[1m]
然後發現版本號每次都要改,乾脆改成 model: opus
後續甚至出了 fable,每次迭代都要自己發現自己修,一次還要改全部的 skill。
是個人都嫌麻煩,乾脆不要追。之後我們全部拔掉,root 立了禁令:

Flow files never name a specific model: normal dispatches inherit the session’s model.

或許甚至連這句話都不需要,純靠 Claude Code 本身自己指派。
模型、版本、路徑,這些外面世界決定的東西都是同一類:
它們變的速度比你改檔案快,寫死一個,就欠一筆遲早要還的債。

效果

禁令之後模型再換代,我們啥都沒有動,享受 0 改動的模型自動增強,好不快活。

3. 求 AI 自我反省的

發生啥事

為了防止開發流程出錯,我們對 agent 設立了跟 user 告知開發完成前要完成自我驗證:

Do not tell the user「完成」/ “done” until every row checks. Rows 1, 4, 5 are the most common failure modes.

還有一份行動前要問自己問題,確保動手前有沒有得到授權。

這些全是之前的每一次出事就加一條的產物:怕漏步驟就列表,怕越權就要它自問,
相信提醒寫得夠清楚,AI 就會照做。
400 行的 tester skill 部分增長的因素也是源自此。

轉捩點

結果完全沒啥用,曾經它自己坦白:檢查表就擺在那裡,第 1、4、5 列還是被跳過,
然後我們再加一句「不要跳」。
主力 skill 就這樣從 130 行長到 571 行。

用描述要求 AI 自我反省,效果跟在牆上貼「請勿奔跑」差不多。
因為都要 AI 判斷自己的狀態。
而這種判斷沒有任何外部證據能核對,它有沒有真的自問,只有它自己知道。

之後我們把思路拆開:這條規則裡,哪些是事實、哪些是判斷?
事實搬進 JSON:驗證指令、測試範圍、清理項目、哪些角色進迴圈,全是欄位。
agent 照抄執行,沒有「我覺得這次不用」、「我覺得這樣好像比較好」的空間。
判斷的依據留在規則檔,但也不再寫成「請你記得自問」,而是寫成一個檔案裡查得到的條件:

planned→go requires a non-empty go: line; developing→dev-done requires every work item done-with-evidence.

你在執行前去看一次某個檔案,沒有值就不執行。
驗證沒過卻直接執行,PLAN.md 上就是證據,retro 一翻就抓到。
兩個動作本質是同一件事:用真實的證據來核對,將沉默換得了可觀測性。

效果

事實的規則收掉了。
全套測試的指令搬進欄位之後,每一次收尾它都有跑,retro 記的只剩它花了多久。

判斷的規則,老實說目前的方式還不足以完全解決問題,畢竟「執行前先去看那一行」這件事,本身也還是一條規則。
望向過往的 retro,還是有被抓到略過驗證擅自執行的紀錄。
但也正因為有 retro,這些事才留得下紀錄,而不是靜默;在此之前,靜默的矛盾基本查不到。

當時我也開始研究怎麼用工具攔下越界操作,例如 Claude Code 的 PreToolUse。
現在已用在部分角色上,這部分留待後面再談。
或許還可參考 Spotify 如何用一整套基建把 agent 圍起來,使正確性不靠 prompt 保證。

4. 前提翻掉的補丁

發生啥事

team lead 的 subagent 曾在回報裡引用不存在的程式碼,因而做出錯誤判斷,於是我們補了一條:

If there is no Read tool call covering the file you are about to cite, STOP. Read first.

要求引用前 team lead 必先親自讀程式碼,當時很合理。

轉捩點

數週後架構變了,team lead 的 context 成了整條流程最貴的資源,自己讀檔等於把腦袋塞滿雜訊。
patch 寫的當下正確,但它依賴的前提被下一次改版整個翻掉,
於是同一件事的規則改成完全掉頭:

never Read source files or full diffs yourself — verifiable facts come from a research dispatch.

team lead 不准自己讀原始碼,事實一律派 subagent 帶出處查回來。
這就是前言說的「叫它可以讀,又叫它不能讀」。
兩條都真實存在過,只是不同時;要是第一條沒被刪乾淨,就是兩條同時在。

效果

同一個問題,前後解法完全相反,兩次都對,差別只在前提。
直到現在,我仍然保留這個分工。

活著的真的有用嗎

誠實來看前面死掉的規則每一條都查得到死因,
但是活著的規則只能證明還沒死。
可我怎麼知道它真的有用?
有些條例判定成本是零、從不礙事,因為只占一行,從來沒有人需要考慮刪它。
便宜的規則因為便宜就放在那直到永生,跟有沒有用無關。

但活著不是免費的。
每個讀它的 agent 都花 token 把它載進腦袋,
每次大掃除都要有人重新判一次留不留。
刪錯的成本看得見,馬上出事;留錯的成本看不見,慢性發作。
然後久而久之就規則爆炸。

所以現在我整理 skill 時都傾向直接刪掉。
有問題再試圖最小針對問題補回。

第一代 skill 的第十行擺著一個字:ultrathink
數月迭代下來,沒有任何一份檢討報告評估過它有沒有效。
它就擺在那,好像真的有用,又好像真的沒用。
而我最近刪掉了它,沒出現什麼問題。

這只是一個例子,擴張到擁有數個 project 的 repo 範圍,我終究得主動處理,我好懶。
死法 3 已經讓 retro 抓得到「略過驗證」,當時我想讓它多記一個維度:這次任務哪幾條規則真的被用到。
後來確實試過這種記錄,也經過調整,過程留到談 retro 時再說。
以前是「要證明它沒用才能刪」,現在是「要證明它有用才能留」。


死掉的規則,每一條都查得到死因;
活著的規則,只能證明它還沒死。

寫這篇文章時,我才想起我有設立一個活著的規則,
它活得好好的,但連維護者本人我都忘記有這件事。