AI 輔助開發與 CLAUDE.md

寫給 AI 看的專案規範,寫了不代表會被遵守。真正決定它有沒有效的是三件事: 規範有沒有對應的機械檢查、彼此有沒有衝突、以及它有多長。

系列・Nuxt 4 全棧實戰筆記・第 12/12 篇

把專案規範寫成一份 AI 讀得到的檔案(Claude Code 的慣例是專案根目錄的 CLAUDE.md),是現在多數團隊的做法。技術棧、目錄約定、禁止事項、語言偏好都寫進去,AI 每次開工前會讀。

它確實有用。但實際跑一段時間之後會發現:規範被違反的次數,跟規範寫得多嚴格沒什麼關係。

有關係的是下面三件事。

一、沒有機械檢查的規則,就是願望清單

在規範裡寫 MUST 和 NEVER,是在對一個機率模型下指令。它多數時候會照做,而「多數時候」在工程上等於「會有例外,而且你不知道是哪一次」。

分界線很清楚:這條規則有沒有一個不靠人也不靠 AI 的東西在把關。

規則把關者實際效力
「程式碼風格要一致」格式化工具 + lint違反就進不了 commit
「所有函式都要設定 search_path」db lint違反就進不了 commit
「不要在 client 端寫入資料庫」沒有靠自律
「複雜任務要分階段確認」沒有靠自律

前兩條是規則,後兩條是期望。兩者都可以寫進規範檔,但心裡要清楚哪些條目在上線那天是真的擋得住的。

實務上的處理方式是:每寫一條新規則,順手問一句「這條能不能變成一個檢查」。能的話就去寫那個檢查,規範檔裡只留一行指向它;不能的話,就接受它是一條靠自律的規則,並且不要因為它寫在檔案裡就假設它一定生效。

你可能會想:那把靠自律的規則寫得更用力一點,全部加上大寫的 MUST 呢?沒有用,而且會反過來稀釋。當一份檔案裡有四十條 MUST,它們就不再是強調,而是背景雜訊——真正致命的那三條跟「檔名請用 kebab-case」長得一模一樣。強調要有效,前提是它稀有。

二、規則彼此衝突時,你不會收到通知

規範檔會長大。長大的過程中,新條目與舊條目撞在一起是常態——A 段說「所有變更都要先建 migration」,B 段又說「快速原型可以直接改再倒出來」。

人讀到會問「那到底哪個算數」,AI 通常不會問,它會挑一條照做。挑哪一條取決於措辭強度、上下文距離這些你控制不了的因素,於是同一份規範在不同的任務裡會產生不同的行為,而這件事在表面上看不出來——每一次它都給了一個看起來合理的結果。

所以規範檔需要定期回頭讀一次全文,看的不是「有沒有漏寫什麼」,而是「有沒有兩條在講同一件事」。發現衝突時,正確的處理是刪掉一條、或明確寫出優先順序,不是兩條都留著再補一條說明。

三、太長的規範會稀釋自己

規範檔跟提示詞共用同一個注意力預算。一份三千行、什麼都寫的規範,實際效果通常比一份三百行、只寫關鍵約束的差——因為重要的那幾條被埋在一堆背景說明裡。

判準是:這一條如果沒寫,會發生什麼壞事? 答不出具體壞事的條目,多半是在描述「我們的風格」而不是在防止問題,那種內容放在給人看的文件裡就好。

同樣的理由,規範檔不適合放教學。「什麼是 RLS」不該在裡面,「這個專案的 RLS policy 一律要包含 service_role 繞過」才該在裡面。

協作本身的兩個界線

除了規範怎麼寫,還有兩件跟任務怎麼交出去有關:

一次交一件事。 「幫我實作完整的使用者管理系統,包含 CRUD、權限、驗證、測試」這種任務會得到一個看起來完整、但每一塊都只做了七成的結果,而且驗收時很難分辨哪一塊沒做完。拆成小任務不只是為了品質,是為了讓「沒做完」看得出來。

驗證不能外包給同一個來源。 產出程式碼的人(或模型)不適合當唯一的驗收者。至少要有一層跑得起來的檢查——測試、型別、lint——它們不會因為推理很流暢就放行。

收束

這一系列前面幾篇談的都是系統的預設值不安全、要靠機制而不是靠記得。規範檔這件事其實是同一個題目的最後一層:

寫下來的規則不會執行自己。 一條規則的實際效力,等於它背後那個不會累、不會忘、不需要被說服的東西——lint、型別檢查、測試、CI。規範檔的價值是把這些東西指出來,而不是取代它們。