Ousterhout 的核心命題:寫程式時,開發者腦中有一堆重要資訊,但程式語言的敘述無法捕捉這些資訊。註解的職責,就是記錄這些程式碼看不出的東西,讓後人能輕鬆理解與修改。指導原則因此很簡單——註解應描述「無法從程式碼推導出來」的事。順著這條線,好註解有兩種增值方式:低層註解補上精確度 (precision),高層註解補上直覺 (intuition)。
🧠 Why It's a Problem
- 資訊會遺失:當初的設計者知道端點含不含、單位是什麼、呼叫順序有沒有限制,但這些多半寫不進型別與敘述裡。不記下來,資訊就隨著人離開而消失。
- 別重複程式碼:把程式碼直接翻譯成一句話的註解毫無價值——它沒帶進任何新資訊,讀者從程式碼本來就看得到。這類註解只是增加要維護的字。
- 抽象需要被說出來:抽象的意義就是把複雜性藏起來。若使用者得靠讀實作才能推出抽象在做什麼,抽象就失效了。唯一的補救是用註解補強對外宣告。
- 「顯而易見」是讀者說了算:對寫的人顯而易見的東西,對第一次讀的人未必。判準永遠站在讀者那一邊。
IMPORTANT
註解的核心承諾:開發者應該光看模組對外的宣告加上註解,就能理解模組提供的抽象——不必讀任何實作程式碼。這也劃出了兩種註解的分工——介面註解 (interface comments) 描述抽象的行為與契約,實作註解 (implementation comments) 才碰內部的運作。
⚖️ 重述程式碼 vs. 補上精確度/直覺
壞 — 註解只是把程式碼再讀一遍
// 把 count 加一
count += 1;
// 走訪 blocks 陣列
for (const block of blocks) {
// 如果 block 是空的就跳過
if (block.isEmpty()) continue;
process(block);
}每一行註解都能從它下面那行程式碼直接讀出來。讀者沒得到任何新資訊,卻多了一批會隨程式碼漂移的字。這種註解不如不寫。
好 — 低層註解補精確度,高層註解補直覺
// 低層註解:補上「程式碼看不出」的精確度——單位、邊界、不變式
// 逾時時間以「毫秒」計;0 代表永不逾時。
let timeout;
// firstLine..lastLine 為「含頭含尾」的閉區間;lastLine 這一行也會被處理。
function render(firstLine, lastLine) { /* ... */ }
// 高層註解:補上「逐行讀不出」的直覺——這段整體在做什麼、為何存在
// 把相鄰、彼此重疊的區段合併成不重疊的區段,
// 讓後續繪製不必處理重複像素。
function coalesce(ranges) { /* 一串迴圈與比較,逐行讀難看出全貌 */ }低層註解回答的是逐行也講不清的細節(毫秒?哪端含?),精確度讓人不必猜。高層註解站得更高,用一兩句給出整段的意圖與效果,直覺讓人不必把每行讀完才懂全局。兩者都帶進了程式碼表面沒有的資訊。
NOTE
精確度與直覺常落在不同高度:越低層的細節(變數、單位、邊界)越需要精確,因為程式碼在這裡最沉默;越高層的區塊(函式、模組)越需要直覺,因為逐行閱讀看不出森林。判斷註解好壞,就看它補的是哪一種——若兩種都不是,它多半只是在重述。
TIP
介面註解只描述抽象——行為、參數、回傳、副作用、契約,不洩漏內部如何實作;否則使用者會依賴實作細節,抽象就漏了。實作註解則寫這段在做什麼、為什麼,而不是逐行複述怎麼做。至於跨越多個模組才成立的設計決策(例如某個不變式必須由兩個類別共同維持),要寫在一個中央、顯眼的地方,而不是分散藏在各處讓人拼不回來。
🔑 Takeaways
- 註解的目的是記下程式碼捕捉不到、但當初存在於設計者腦中的資訊。
- 不要重述程式碼;好註解一定帶進新資訊——低層補精確度,高層補直覺。
- 介面註解描述抽象(行為與契約),實作註解描述做什麼與為什麼;別讓實作細節漏進介面。
- 跨模組的設計決策集中寫在一處,別散落各地。
- 「顯而易見」以讀者為準——這頁談的是註解該攜帶什麼資訊;至於「解釋為什麼、別留註解掉的程式碼」,見姊妹頁 好註解解釋「為什麼」。
No notes yet — jot your takeaways or Q&A here.