註解是程式碼品質的警訊#
本章討論的是方法內部的註解,不包含 Javadoc 之類供外部工具使用的文件。
Rob Pike 早在 1989 年就指出:如果程式碼夠清晰、命名夠好,它應該能自我解釋。註解不受編譯器檢查,容易在修改後失去同步,而誤導性的註解比沒有註解更危險。Martin Fowler 則將註解列為 code smell,認為它們常被當作「程式碼除臭劑」——與其在臭程式碼上噴香水,不如直接清洗乾淨。
Kevlin Henney 的原則:「只為程式碼無法表達的事情寫註解(Comment only what the code cannot say)。」
本章將註解分為五類,從最容易處理到最困難,依序討論。
flowchart TD
Start["遇到註解"] --> Q1{"過時或不正確?"}
Q1 -->|是| DEL1["刪除"]
Q1 -->|否| Q2{"被註解掉的程式碼?"}
Q2 -->|是| DEL2["刪除"]
Q2 -->|否| Q3{"顯而易見?"}
Q3 -->|是| DEL3["刪除"]
Q3 -->|否| Q4{"描述接下來的程式碼?"}
Q4 -->|是| EXT["抽取為方法\n用註解當方法名稱"]
Q4 -->|否| Q5{"記錄不變量?"}
Q5 -->|是| Q6{"能用型別或測試取代?"}
Q6 -->|是| REP["用型別或測試取代"]
Q6 -->|否| KEEP["保留註解"]
style DEL1 fill:#c8e6c9
style DEL2 fill:#c8e6c9
style DEL3 fill:#c8e6c9
style EXT fill:#bbdefb
style REP fill:#bbdefb
style KEEP fill:#fff9c4第一類:刪除過時的註解#
過時的註解包括已經不正確或具有誤導性的內容。它們曾經正確,但隨著程式碼演進而失去同步。
| 嚴重程度 | 後果 |
|---|---|
| 最輕微 | 浪費閱讀時間 |
| 中等 | 誤導開發者設計出錯誤的程式碼 |
| 最嚴重 | 直接導致 bug |
例如,註解寫著「has a selection and allows multi selection」,但條件判斷用的是 ||(or)——這種不一致會造成危險。
處理方式:直接刪除。
第二類:刪除被註解掉的程式碼#
開發過程中,暫時將程式碼註解掉來實驗是合理的。但實驗結束後,被註解掉的程式碼應該被刪除,因為版本控制系統隨時可以還原。
正確的實驗流程是:
- 在 Git 建立分支
- 刪除舊程式碼、撰寫新程式碼
- 成功就合併,失敗就丟棄分支
處理方式:直接刪除,需要時從版本歷史還原。
第三類:刪除顯而易見的註解#
當程式碼本身和註解一樣容易閱讀時,這個註解就是多餘的(trivial)。
例如,在 Logger.error(errorMessage, e) 上方寫 /// Log error——這完全沒有增加任何資訊量。同樣地,如果一個註解在瀏覽程式碼時總是被跳過,它就只是在佔空間。
處理方式:直接刪除。
第四類:將註解轉化為方法名稱#
有些註解描述的是接下來那段程式碼在做什麼。例如:
/// Build request url
if (queryString)
fullUrl += "?" + queryString;這種情況下,應該將那段程式碼抽取為方法,用註解的內容作為方法名稱。抽取後原來的註解就變成顯而易見的,可以刪除。
有人不喜歡太長的方法名稱,但這只有在頻繁呼叫的方法上才是問題。就像自然語言一樣——越常用的詞越短,程式碼庫也應該如此。
以註解做規劃是好習慣#
在開發時用註解列出計畫步驟(如 /// Fetch data、/// Transform)是很好的做法。但程式碼寫完後,必須審視這些註解是否仍有價值——有些會變成顯而易見的註解,有些應該轉化為方法名稱。
第五類:保留記錄不變量的註解#
最後一類是記錄**非局部不變量(non-local invariant)**的註解。辨識方法是問自己:「這個註解是否能防止某人引入 bug?」
例如:
/// Log off used to force re-authentication on next request
session.logout();身份驗證的交互行為極難測試或模擬,這樣的註解完全合理。
遇到這類註解時,仍應依序嘗試:
- 能否用編譯器(型別系統)取代?
- 能否用自動化測試驗證?
- 以上都不可行,才保留註解
流程中的不變量#
TODO、FIXME、HACK 也是一種不變量——不是程式碼的不變量,而是開發流程的不變量。有人主張它們應該放在工單系統而非程式碼中,這確實有道理。但如果選擇放在程式碼裡,就應該:
- 有視覺化方式追蹤數量
- 數量必須持續下降
- 目標是真正修復問題,而不是無限期推遲
註解在開發過程中非常有用,但應在交付前的重構階段處理。交付的每一行註解,都應該先問過自己:「有沒有更好的方式表達它所說的事?」