安安~我是ChiYu~
昨天,我們把 Work Item 逾期流程的命名規則寫進 Repository,讓 Agent 可以在安全範圍內自主命名。
名稱說清楚之後,我接著測另一個很常見的要求:補註解、整理格式,真的會讓程式碼更好讀嗎?
這類工作看起來很適合交給 AI。只要一句「幫我補上中文註解,順便整理格式」,Agent 很快就能補齊文字、修正縮排;Build 與測試也全綠時,很容易讓人覺得整理已經完成。
實際跑完後,一般 Prompt 留下五則註解,其中三則只是把 Code 翻成中文;加入 Comment Policy 後,只剩兩則有測試依據的警告。更麻煩的是,我把其中一則警告改成相反意思,所有程式驗證仍然通過,下一個 Agent 的工作終點卻跟著改變。
所以今天要分開處理兩件事:註解是否提供可信的新資訊,以及 Formatter 到底能替我們整理到哪裡。
實驗會先比較一般提示與 Comment Policy 的輸出,再故意把正確註解改錯,觀察下一個 Agent 的判斷路徑;最後交給 Formatter,確認哪些工作真的能自動化。過程中,我會用 E — Explicit Intent and Boundaries 意圖明確,替程式碼、註解、測試與工具劃清責任。
〈註解〉先提醒我們:能透過命名、型別、函式與程式結構表達的內容,應該優先寫進程式碼。不要用註解替混亂的 Code 善後,因為程式會繼續執行,旁邊的自然語言卻可能停在舊版本。
不過,有些限制無法只靠名稱與結構交代,例如法律資訊、公開 API 文件、外部副作用、相容性要求與特殊失敗路徑,這些內容仍需要註解補位。判斷重點始終是資訊價值,不是註解數量。
我先把這次會碰到的資訊分工整理如下:
| 資訊 | 優先放在哪裡? | 本次案例 |
|---|---|---|
| 系統正在做什麼 | 名稱、型別與程式結構 | 查出到期項目、修改逾期狀態、嘗試通知、儲存 |
| 難以直接看出的限制或警告 | 靠近修改點的行內註解 | 重跑仍須通知;通知必須早於儲存 |
| 公開方法的用途、參數與回傳契約 | XML 文件註解,也就是可供 IDE 與文件工具讀取的 /// 結構化文件 |
ProcessOverdue 的責任與回傳摘要 |
| 必須持續成立的可觀察行為 | 自動化測試 | 通知失敗仍儲存、重跑通知、呼叫順序 |
| 跨越多個版本的設計理由 | 架構決策紀錄或設計文件 | 為何選擇某種通知一致性策略 |
如果 currentUtc 已經說明這是 UTC 時間,再補一句「取得目前 UTC 時間」,只是在翻譯 C#。相反地,「已經是 Overdue 的項目重跑時仍須通知」並不直覺,也很容易在重構時被順手移除,這才值得留下一則短警告。
〈編排〉同時處理水平與垂直兩種閱讀關係。水平編排是空白、縮排與一行內容如何排列;垂直編排則是相關內容是否靠近、不同概念是否分開,以及檔案能不能從高階流程一路讀到實作細節。
.editorconfig 記錄 Repository 的格式與樣式規則,Formatter 再依照這些設定自動整理 Code。空白、換行與部分 Code Style 可以交給工具;函式是否應該相鄰、閱讀順序是否合理,仍然是設計判斷。
如果把程式碼想成道路,Formatter 負責把已經規定好的標線畫整齊,註解則像路標。標線再漂亮,也無法判斷路標是不是指向錯誤方向;路標的真假,也不能靠重新鋪柏油驗證。
Agent 會把附近的名稱、型別、註解、測試與 Repository 規則一起納入 Context。在這次實驗中,正確的警告註解能和其他證據互相印證,讓 Agent 排除不符合既有行為的修改方向。例如看到「重複執行仍須再次通知」,它就知道不能把通知移進「本次才剛改成 Overdue」的條件裡。
失效註解則是已經不符合目前行為的舊說明。這次它讓下一個 Agent 停下來,比對 Production Code、測試與 Repository 規則;若缺少其他證據,Agent 是否會直接採信錯誤註解,仍然只能列為風險,不能寫成這次已經發生的結果。
本篇也沒有進行可比較的 Token 實驗,因此只討論理解路徑,不換算節省比例。好註解的直接價值,是用很少的文字補上一項非直覺限制;逐行翻譯則只會占用閱讀空間與模型 Context,卻沒有增加新的判斷依據。
E — Explicit Intent and Boundaries 意圖明確:先替每種資訊分配責任在這篇,E — Explicit Intent and Boundaries 意圖明確 負責劃分五類資訊的去處:
有了這些判準,Agent 仍然可以自行決定是否需要註解,卻不能替 Repository 發明原因。遇到證據衝突時,也不能挑一個最方便的答案繼續修改。
四組實驗共用相同的 Production Code 與既有行為,各自改變一項主要條件:提示規則、註解內容或格式工具。第二種情境會加入 Comment Policy,也就是 Repository 對註解用途、證據與停止條件的共同規則。
| 實驗 | 主要刻意改變的內容 | 為什麼這樣設計? | 可以觀察什麼? |
|---|---|---|---|
| 一般 Prompt 與 Comment Policy | User 是否定義註解責任與證據門檻 | 觀察缺少明確判準時,這次候選保留了哪些文字;加入政策後,Agent 又如何處理重述型註解 | 兩份候選的註解類型與保留理由 |
| 正確註解與相反註解 | 只改一行自然語言,正式程式碼與測試保持相同 | 隔離自動化關卡能否驗證註解真偽 | Build、Test、Format 與 Smoke 的能力邊界 |
| 下游 Agent A/B | 同一項任務只讀到不同註解 | 觀察失效註解是否會改變下一個 Agent 的判斷路徑 | Agent 能完成判斷,或因證據衝突停止 |
| Formatter 對照 | 只改空白或縮排 | 檢查工具實際能修哪些格式 | 本 Repository 的 .editorconfig 與 dotnet format 能力 |
所有 Agent 都使用 Codex GPT-5.6-SOL-HIGH,並從同一份程式碼出發。註解候選只能修改 WorkItemsController.cs 的註解與排版,不能改名稱、控制流程、測試、公開契約與產品行為。
前兩種 Prompt 各執行一次,因此只能比較這兩份候選,不能推論 Comment Policy 必然減少固定數量的註解,也不能代表其他 Repository 會得到相同結果。
第一種情境只要求 Agent「補上有幫助的註解」,不提供保留標準。我要觀察的是,缺少明確判準時,Agent 會留下哪些文字,而不是它能不能寫出中文註解。
你正在 Day 6 註解與 Formatting 實驗的隔離 Worktree。請先閱讀 AGENTS.md、WorkItemsController.ProcessOverdue、相關行為測試與 .editorconfig。
任務:讓 WorkItemsController.ProcessOverdue 更容易閱讀。請替每個主要步驟補上你認為有幫助的註解,並整理這段流程的格式與留白。註解的種類、數量與位置由你判斷。
只允許修改 src/WorkItems.Api/WorkItemsController.cs 的註解與排版。不得重新命名識別字、改變控制流程、條件式、方法內容、方法責任、字串、HTTP Route、公開 DTO、JSON 欄位、錯誤訊息、測試、套件、Schema 或產品行為。不要新增、刪除或搬移方法、型別與檔案。若改善必須超出範圍,停止並說明。
完成後執行 Repository 規定的相關驗證,回報新增或修改的註解、格式差異、實際 Gate 與剩餘風險。不要 Commit。
這次 Run 產生的候選共新增 5 則註解,也把 LINQ 條件與過長的 Helper 呼叫拆成多行:
// 固定本次批次的時間基準,只選取已到期且尚未完成的項目。
var currentUtc = timeProvider.GetUtcNow();
var dueIncompleteWorkItems = await database.WorkItems
.Where(workItem =>
workItem.Status != "Completed" &&
workItem.DueAtUtc <= currentUtc)
.ToListAsync(cancellationToken);
// 狀態轉換、通知嘗試與通知失敗分開統計,避免混淆各自的摘要語意。
var overdueStatusChangeCount = 0;
var notificationAttemptCount = 0;
var notificationFailureCount = 0;
// 每個符合條件的項目都會嘗試通知;原本已逾期的項目也會再次通知。
foreach (var workItem in dueIncompleteWorkItems)
{
// ...
}
// 所有通知嘗試完成後才儲存狀態,維持通知早於資料寫入的既有順序。
await database.SaveChangesAsync(cancellationToken);
// ProcessedCount 只計算本次實際轉為 Overdue 的項目。
return Ok(new ProcessOverdueResponse(/* ... */));
五則註解都正確,價值卻不相同。固定時間、三種計數與 ProcessedCount 已經能從名稱和 Code 讀出;重跑通知與通知早於儲存,則會影響下一次修改,而且不是一眼就能看出的限制。
| 註解 | Code 已經提供的資訊 | 審查結果 |
|---|---|---|
| 固定時間基準、選取到期未完成項目 | currentUtc、dueIncompleteWorkItems 與 LINQ 條件 |
重述,不保留 |
| 三種計數分開統計 | 三個計數器名稱已逐一說明 | 摘要,不保留 |
| 已逾期項目仍會再次通知 | Where 只排除 Completed,但意圖不直覺 |
警告,保留 |
| 通知完成後才儲存 | 呼叫順序看得到,不能輕易改動的契約不明顯 | 警告,保留 |
ProcessedCount 只算狀態轉換 |
變數名稱、條件式與 Response XML 已說明 | 重述,不保留 |
前三則內容雖然正確,卻沒有提供 Code 以外的新資訊。名稱或結構改變時,團隊還得同步維護同一件事的中文版本。真正值得保留的,是後兩則能阻止錯誤修改的警告。
第二種情境直接在 Prompt 提供 Comment Policy,要求 Agent 只保留警告、限制、相容性原因與無法由 Code 表達的資訊。這次不指定要寫幾則,也允許 Agent 在沒有必要資訊時回傳零 Diff。
你正在 Day 6 註解與 Formatting 實驗的隔離 Worktree。請先閱讀 AGENTS.md、WorkItemsController.ProcessOverdue、相關行為測試與 .editorconfig。
任務:讓 WorkItemsController.ProcessOverdue 更容易閱讀,但請遵守以下 Comment Policy:
1. 只有程式碼無法直接表達、而且能由 Repository 證明的原因、限制或警告,才保留註解。
2. 不逐行翻譯程式碼,不用註解重述方法名稱、變數名稱、條件式或呼叫順序。
3. 不替既有行為發明需求、設計動機或歷史原因;找不到證據就不寫。
4. 註解必須靠近它約束的程式,並以未來維護者需要避免的錯誤為焦點。
5. 如果名稱與結構已足夠,允許不新增任何註解;不要為了交付 Diff 而製造註解。
6. 格式與留白只依 .editorconfig 及 dotnet format,不能把 Format Gate 全綠當成可讀性結論。
請特別確認重複執行時的通知行為、通知與儲存的順序,以及通知失敗後的狀態儲存。若其中確實存在單靠名稱與結構不容易看出的既有約束,可以留下最少量、可由測試證明的警告註解。
只允許修改 src/WorkItems.Api/WorkItemsController.cs 的註解與排版。不得重新命名識別字、改變控制流程、條件式、方法內容、方法責任、字串、HTTP Route、公開 DTO、JSON 欄位、錯誤訊息、測試、套件、Schema 或產品行為。不要新增、刪除或搬移方法、型別與檔案。若改善必須超出範圍,停止並說明。
完成後執行 Repository 規定的相關驗證,回報保留或拒絕的註解、Repository 證據、格式差異、實際 Gate 與剩餘風險。不要 Commit。
第二次 Run 產生的候選只留下兩則警告:
// 不可排除已是 Overdue 的項目;重複執行仍須再次嘗試通知。
var dueIncompleteWorkItems = await database.WorkItems
.Where(workItem => workItem.Status != "Completed" && workItem.DueAtUtc <= currentUtc)
.ToListAsync(cancellationToken);
// 通知必須先於狀態儲存;即使通知回報失敗,仍須儲存 Overdue 狀態。
await database.SaveChangesAsync(cancellationToken);
第一則可以追到重複執行測試;第二則可以追到通知失敗與通知早於儲存的測試。Agent 沒有替計數器、輔助函式呼叫、條件式與 Response 補上中文翻譯,因為這些位置的名稱與結構已經表達行為。
兩份候選放在一起,差異如下:
| 比較項目 | 一般補註解候選 | Comment Policy 候選 |
|---|---|---|
| 新增註解 | 5 則 | 2 則 |
| 可直接由 Code 讀出的重述 | 3 則 | 0 |
| 可追到行為測試的警告 | 2 則 | 2 則 |
| 找不到理由證據時 | Agent 自行判斷是否補充 | 明確禁止發明原因,可回傳零 Diff |
| 最終決策 | 保留實驗,不整包採用 | 接受為今天的系列接續版本 |
在這兩次 Run 中,提供 Comment Policy 的候選少了三則重述型註解。由於每組只執行一次,我接受的是 Policy 提供的判斷方式,不把註解數量差異解讀成固定因果。
| 註解類型 | 範例 | 處理方式 |
|---|---|---|
| 把單行程式翻成中文 | 「取得目前 UTC 時間」 | 名稱或單行程式已經完整表達,刪除 |
| 替整段流程再做一次摘要 | 「統計狀態轉換、通知嘗試與失敗」 | 名稱與結構已經表達;沒有形成公開契約時,刪除 |
| 替現況發明理由 | 「為了避免資料庫鎖定,所以先通知再儲存」 | Repository 只能證明先後順序,不能證明設計動機,拒絕寫入 |
| 提醒可驗證的限制 | 「重複執行仍須通知」 | 行為不直覺、容易被改壞,而且能追到測試,保留 |
其中最需要小心的是「替現況發明理由」。如果 Repository 的 ADR、Issue、需求或測試都無法證明資料庫鎖定就是原因,就不該把推測寫成註解。後續 Agent 可能會把這類敘述當成已確認的架構決策,反而增加誤判風險。

圖:先問程式碼是否已能表達;只有具證據的 Why、限制或警告,才增加一則最小註解。
下一組實驗只改一件事:把正確的警告註解翻成相反意思,Production Code 完全不動。這和 Mutation Testing 不同;Mutation Testing 會修改可執行程式碼,再檢查測試能不能抓到錯誤,這次修改的是 Agent 會讀到的自然語言。
- // 不可排除已是 Overdue 的項目;重複執行仍須再次嘗試通知。
+ // 只對本次剛轉成 Overdue 的項目嘗試通知,重複執行不會再次通知。
修改後,註解要求重複執行時不要再次通知,Production Code 卻仍會照常送出。重新執行 Build、測試、Format 與 HTTP Smoke 後,所有程式驗證仍然通過。HTTP Smoke 對同一批工作項目連續呼叫兩次,結果如下:
| 呼叫 | 本次改成 Overdue 的數量 | 通知嘗試次數 | 代表什麼? |
|---|---|---|---|
| 第一次 | 2 | 2 | 兩筆到期項目改成 Overdue,也各通知一次 |
| 第二次 | 0 | 2 | 狀態不需再改,但既有行為仍要求再次通知 |
程式與測試清楚顯示第二次仍會通知,那則錯誤註解卻照樣留在原地。
Build、測試、Format 與 HTTP Smoke 檢查的是可執行行為與格式,一般行內註解不參與 C# 執行。全綠只能說已涵蓋的 Code 行為沒有漂移,無法替旁邊的自然語言背書。
接下來要驗證的是:錯誤註解會不會改變下一個 Agent 的判斷路徑。我建立兩個隔離 Worktree,兩邊使用相同的 Codex GPT-5.6-SOL-HIGH、Prompt、Production Code、測試、AGENTS.md 與 Smoke,只有查詢前的註解內容不同。
這裡用到 Characterization Test,也就是先把系統目前的可觀察行為固定下來,再進行整理。它不代表現況永遠正確,但能避免 Agent 在沒有新需求的情況下,順手改掉原本受保護的行為。
| 組別 | Agent 讀到的註解 |
|---|---|
| 正確註解組 | 不可排除已是 Overdue 的項目;重複執行仍須再次嘗試通知。 |
| 失效註解組 | 只對本次剛轉成 Overdue 的項目嘗試通知,重複執行不會再次通知。 |
兩組收到的完整任務指令如下:
你正在 Work Item API 的通知判斷局部整理實驗中。請先閱讀 AGENTS.md、WorkItemsController.ProcessOverdue、ApplyOverdueStatusAndAttemptNotificationAsync、ProcessOverdueBehaviorTests 與 scripts/run-series-baseline-smoke.ps1。
任務:請檢查並局部整理 WorkItemsController.ProcessOverdue 的通知判斷,目標是減少「不必要的通知呼叫」,同時完整保留 Repository 目前受保護的產品行為與公開契約。
只有 Repository 的 Production Code、測試、AGENTS.md 與 Smoke 能共同證明某次通知不必要時,才可以修改。若註解、Production Code、測試、AGENTS.md 或 Smoke 描述的通知行為互相矛盾,立即停止修改並精確列出衝突;不得自行選一邊,也不得修改測試期待來配合實作。
只允許修改 src/WorkItems.Api/WorkItemsController.cs。不得修改測試、AGENTS.md、指令碼、公開 DTO、JSON 欄位、HTTP Route、Schema、套件或其他產品行為。若沒有可安全移除的通知呼叫,允許回傳零 Diff。
完成後執行 Repository 規定的相關驗證,回報你採信的 Repository 證據、實際 Diff、執行的 Gate 與剩餘風險。不要 Commit。
正確註解組確認註解與 Production Code、行為測試、Smoke、AGENTS.md 一致,重跑再次通知是受保護行為。它沒有找到能安全移除的呼叫,因此回傳零 Diff。
失效註解組走到同一段 Code 時,發現註解和 Production Code、行為測試、Smoke、AGENTS.md 對不上。它依照停止條件列出衝突,同樣回傳零 Diff。
| 實際結果 | 正確註解組 | 失效註解組 |
|---|---|---|
| Repository 證據 | 註解與其餘四項證據一致 | 註解與其餘四項證據衝突 |
| Agent 決策 | 沒有不必要的通知可移除 | 停止修改並列出衝突 |
| Production Diff | 0 | 0 |
| 任務是否完成 | 完成零 Diff 判定 | 無法繼續整理 |
我從兩個 Worktree 外部重跑相同驗證,確認兩組 API 的執行行為沒有差異。正確註解組完成「沒有安全修改可做」的零 Diff 判斷;失效註解組則因證據衝突而停止,必須等 User 釐清。
在這次案例中,註解真偽改變了 Agent 的查證路徑與工作終點,但沒有產生錯誤 Code。
如果 Repository 沒有 Characterization Test、Smoke 與停止條件,Agent 就少了反駁註解的依據。那時它可能把自然語言當成需求,將通知移進「本次狀態剛改變」的條件裡。這是根據本次證據做出的風險推論,不是這次實驗已經產生的版本。
兩次 Agent 執行的 Token 與時間也不能直接比較。失效註解組較早觸發停止條件,正確註解組則繼續驗證,兩邊做的工作量不同。這次有效的觀察只有一項:一行註解改變了工作路徑與終點。
正確組的註解與 Production Code、測試及 Repository Instruction 互相支持,因此 Agent 能完成零 Diff 判定;失效組則必須先停下來處理衝突。
兩組最後都沒有修改 Production Code,看起來結果一樣,判斷卻完全不同。前者證明目前沒有安全修改可做;後者只證明 Repository 內部的資訊已經互相矛盾,不能再往下猜。
這裡說的「從零開始」,只針對方法內部的行內註解,不包含公開 API 的 XML 文件註解。我不替 Repository 訂「每個方法至少幾則」或「每十行一則」的比例,因為數量目標很容易把註解變成逐行翻譯。
每次想新增行內註解時,我會先問四個問題:
| 判斷結果 | 行內註解建議 | 處理方式 |
|---|---|---|
| Code 已經能表達 | 0 則 | 不重述步驟,也不替每個區塊加摘要 |
| 有一項容易被改壞的非直覺限制 | 1 則短警告 | 靠近修改點,並能追到測試或契約 |
| 有多項彼此獨立的限制 | 每項各 1 則 | 不把不同警告塞進同一大段背景故事 |
| 需要多段歷史與設計理由 | Code 旁只留摘要與證據座標 | 完整內容放進 ADR、Issue 或設計文件 |
| 找不到 Repository 證據 | 0 則 | 不替現況發明原因,必要時交回 User |
我自己的判斷門檻是:一則行內註解只處理一項重要限制。若需要整段文字才能說清楚,通常代表名稱、函式或文件邊界還有整理空間;Code 旁可以留下簡短警告,再把完整理由移到 ADR、公開契約或設計文件。
註解本身也會占用 Context,「寫得越完整」不會自動替 AI 省 Token。短而高資訊量的註解,比較有機會阻止 Agent 展開錯誤路徑、重讀更多檔案,或把已知限制重新推理一次;最後能不能省下 Token,仍取決於任務、搜尋策略與 Repository 大小。
因此,我習慣讓行內註解從零開始。每發現一個 Code 難以直接表達、容易誤判,又有證據支持的限制,才增加一則最小警告。這是依風險增減,不是固定配額。
我仍習慣替公開類別與函式補上 XML 註解,因為人類偶爾還是要回來找 Code。從 IntelliSense、符號清單或方法定義看到摘要時,可以更快判斷這是不是自己要找的區塊,不必先讀完整個實作。
其中 Controller Action 我一定會補。Action 是外部 API 契約的入口,<summary> 說明它負責什麼,<param> 與 <returns> 補上輸入和回傳語意。這些內容除了方便 IDE 閱讀,也能成為 OpenAPI 文件的來源。這是我的個人慣例,不是 Clean Code 規定每個方法都必須有 XML 註解;文件仍要提供新資訊,而且必須持續正確。以這次的 ProcessOverdue 為例:
/// <summary>
/// 處理目前已到期且尚未完成的工作項目。
/// </summary>
/// <param name="cancellationToken">取消權杖。</param>
/// <returns>本次狀態轉換與通知嘗試摘要。</returns>
[HttpPost("process-overdue")]
public async Task<ActionResult<ProcessOverdueResponse>> ProcessOverdue(
CancellationToken cancellationToken)
不過,只寫 /// 不代表 OpenAPI JSON 一定會出現說明。以目前的 .NET 10 Demo 為例,專案還要啟用 XML 文件輸出:
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
再搭配標準的 AddOpenApi() 註冊方式,OpenAPI 產生器才會把支援的 XML 文件標籤帶進規格。若專案使用 Swashbuckle,則要另外設定 IncludeXmlComments 載入產生的 XML 文件。
若使用 <response code="..."> 描述某個 HTTP 狀態,仍要透過回應型別中繼資料或 [ProducesResponseType] 宣告該狀態。XML 文字只能補充說明,不能單獨創造 API 回應契約。
這個 Demo 已經使用 AddOpenApi(),Controller Action 也都有 XML 註解,但 .csproj 尚未設定 GenerateDocumentationFile。因此,這些文字目前只服務程式碼閱讀與 IDE,還沒有進入產生的 OpenAPI JSON。若要同步到 API 文件,必須先補齊專案設定,再實際檢查輸出結果。
XML 註解說明公開契約,行內警告補充實作限制;兩者都不該只是把 Code 再翻譯一次。
dotnet format 能修空白,無法替團隊決定編排註解實驗完成後,我再安排兩個只改空白或縮排的格式對照,確認工具實際負責到哪裡。
第一次把正常的運算子空白拿掉:
- var currentUtc = timeProvider.GetUtcNow();
+ var currentUtc=timeProvider.GetUtcNow();
執行 dotnet format 後,空白被恢復,Diff 回到 0。這類規則已經寫進工具能理解的 Code Style,適合交給 Formatter 自動處理。
第二次,我把鏈式呼叫的 .Where(...) 故意退到明顯不自然的縮排:
var dueIncompleteWorkItems = await database.WorkItems
- .Where(workItem => workItem.Status != "Completed" && workItem.DueAtUtc <= currentUtc)
+ .Where(workItem => workItem.Status != "Completed" && workItem.DueAtUtc <= currentUtc)
.ToListAsync(cancellationToken);
這次 dotnet format 仍成功結束,縮排卻沒有被修正。在目前的 .editorconfig 與 Analyzer 設定下,Formatter 能修正運算子空白,沒有涵蓋這種鏈式呼叫縮排。
這個結果很直接:Formatter 只能執行已設定而且工具支援的規則。註解真假、函式位置與垂直閱讀順序,都不在它的判斷範圍內。
格式是 AI Coding 中較適合交給工具的一環。能自動執行的規則,優先放進 .editorconfig、Analyzer 或 CI;若專案另有版權檔頭、特殊排版或特定資料夾格式,再把例外明確寫進 Repository Instruction。User 要確認的是規則有沒有留下來,以及工具是否真的涵蓋它。

圖:Formatter 能執行已配置的空白與樣式規則,無法判斷註解真假、責任位置與閱讀順序。
前天先留下 Refactoring Scope Policy,昨天再加入 Repository Naming Policy。今天則把註解的保留條件、證據門檻與停止條件整理成 Comment Policy。
每次重新貼上相同規則,既浪費 Prompt 篇幅,也容易在不同 Session 漂移。我先把本次校準結果放進 AGENTS.md,讓後續 Agent 都能讀到;等判斷流程成熟後,再抽成可跨 Repository 重複使用的 Skill。
## Comment Policy
- 名稱與程式結構必須先表達主要行為,不用註解補償模糊命名或混合責任。
- 行內註解只保存 Code 無法直接表達的原因、限制或警告。
- XML 文件註解描述公開責任、參數與回傳契約;Controller Action 必須依專案規範補齊,並驗證文件產生器是否真的將內容納入 OpenAPI。
- 預設不新增行內註解;每則保留的註解都必須降低一個可以指認的誤判風險。
- 一項限制只留一則短警告;需要多段背景才能說明時,行內只保留摘要與 ADR、Issue、測試或契約座標。
- 每則新增或修改的行內註解,都必須指出可追溯的 Repository 證據,例如測試、公開契約、ADR 或已核准需求。
- 不逐行翻譯程式碼,不重述方法名稱、變數名稱、條件式與呼叫順序。
- 找不到原因證據時不得自行補完;可以回傳零註解 Diff。
- 註解與 Production Code、測試、公開契約或 Smoke 互相矛盾時,立即停止,不得自行選一邊。
- 行為變更時,同一項任務必須檢查受影響註解;Build、Test 與 Format 全綠不能替註解內容背書。
Comment Policy 還需要提供清楚的決策路徑,讓 Agent 知道不同資訊該往哪裡走:
| 遇到的資訊 | Agent 的處理方式 |
|---|---|
| Code 本身就能表達 | 改善名稱或結構,不新增行內註解 |
| 公開 API 契約 | 使用 XML 文件註解,並驗證文件產生器 |
| 非直覺且可驗證的限制 | 留下最小行內警告,附上 Repository 證據 |
| 需要長篇歷史背景 | 移到 ADR 或設計文件,Code 旁只留摘要與座標 |
| 空白、縮排與 Code Style | 交給 .editorconfig、Formatter 或 Analyzer |
| 註解與可執行證據衝突 | 停止修改,列出矛盾交回 User |
Agent 可以先依照這份路徑完成分類與證據查核;User 的 Review 則集中在兩個高風險位置:Repository 尚未證明的理由,以及行為改變後沒有同步更新的文字。
本系列接下來沿用 Comment Policy 候選,只留下兩則警告:
我接受 Comment Policy 候選,因為留下的兩則註解都符合三個條件:Code 本身看不出來、內容會影響修改決策,而且有測試可以驗證。
一般候選的另外三則只是在重述 Code,因此不採用。若未來其中一項變成公開契約、相容性限制或特殊副作用,值得保留的註解數量也應重新評估。Policy 提供的是決策條件,不會把今天的答案永久寫死。
回到開頭的問題,補註解、整理格式,確實能讓程式碼變得更整齊,卻不保證資訊更可信。
一般提示留下五則註解,Comment Policy 候選只保留兩則有證據支撐的警告;失效註解沒有破壞任何程式測試,卻改變了下一個 Agent 的驗證終點。E — Explicit Intent and Boundaries 意圖明確 在這裡劃清資訊責任、證據門檻與停止條件,讓 Agent 不必用逐行翻譯湊出「完整註解」。
我最後留下的分工很簡單:名稱、型別與結構先說明主要行為;行內註解補上有證據的非直覺限制;XML 文件註解負責公開契約;格式工具執行團隊已設定的機械規則。
這些結果不代表兩則註解永遠最乾淨,也無法換算固定的 Token 節省比例。它們支持的是:AI Coding 的註解品質,要看它是否減少猜測,而不是看數量多寡或版面是否整齊。
註解與格式整理完後,ProcessOverdue 仍然塞著查詢、迴圈、統計、通知與儲存。明天要繼續驗證:當 Agent 拆分函式時,怎麼避免只是把長函式換成一連串難以追蹤的跳轉?
實驗起點是昨天接受的命名版本。讀者不需要先把專案下載到本機,也可以直接查看下方 Diff 與公開實驗紀錄;想在本機重現時,再執行:
git fetch origin --tags
git switch --detach day-05-sol-high-policy-driven-run-01
git rev-parse HEAD
3b98bbd4b20b4863d1eb127f16e1f4cd996c5240/Tag day-05-sol-high-policy-driven-run-01。ba96751962ef8005d1bf12cf4788aa017da055b3/Tag day-06-sol-high-vague-comments-run-01。1583af33b5e517530871ff3ee724cdcad4e4c5c0/Tag day-06-comments-formatting。569a400559a6986e4c6dea4ac9414fedc4e64c65/Tag day-06-stale-comment-mutation。dotnet format command。