多數人把寫註解拖到最後——編碼與測試都做完才補。這幾乎保證寫出爛文件。真正的時機是一開始:在動手寫程式碼之前,先為類別與方法寫下介面註解。這麼做不只讓文件更好,更關鍵的是——它把註解變成一種設計工具,讓你在寫下任何實作前就審視自己的抽象。
🧠 為什麼先寫
- 延後 = 品質崩壞:「等程式碼穩定再寫」聽起來合理,但拖一拖就再拖,未寫文件的程式碼越積越多,「停幾天補完」永遠不是好時機。就算真的回頭補,你心理上已經結案、急著做下一件事、設計的記憶也開始模糊——於是你邊看程式碼邊寫註解,寫出來的就只是重複程式碼。缺漏的剛好是文件最該描述的那些事(當初的設計理由)。
- 註解是唯一能完整捕捉抽象的工具:而好的抽象是好設計的基礎。在寫實作之前先描述抽象,你就能在還沒被程式碼綁住時審視、調整它。
- 逼你先想清楚本質:要寫好一段介面註解,你得先問「這個實體最重要的是什麼?」。早期就想清楚,你就不再只是亂打程式碼,而是帶著設計意圖前進。
- 成本其實極低:打字(含修改)大概不到總開發時間的 10%,就算一半是註解,寫註解也只佔約 5%。而先寫註解讓抽象在寫程式前就更穩定,省下的程式碼修改成本可能讓它整體上甚至更快。
IMPORTANT
「先寫註解」最重要的好處不是文件,而是改善系統設計。註解在這裡的角色是設計的鏡子:你對某個抽象描述得越吃力,就越說明這個抽象本身有問題。
⚖️ 把註解當設計步驟
作者的流程是:新類別先寫類別介面註解;再寫最重要 public 方法的介面註解與簽章(主體留空);在這些註解之間反覆迭代,直到結構大致對勁;接著才填入方法主體。程式碼完成時,註解就完成了——永遠不會有「待寫文件」的積欠。
先寫介面註解(主體留空),拿它來審視抽象
// 先寫下這個抽象「對外承諾什麼」,此時一行實作都還沒有。
// 目標:用最少的字,完整而清楚地描述介面。
/**
* 一個容量固定的環狀緩衝區:寫滿後,新資料覆蓋最舊的資料。
* 讀取一律從最舊到最新。此類別不做同步,呼叫端自行負責。
*/
class RingBuffer {
/** 建立一個最多容納 capacity 筆元素的緩衝區。 */
constructor(capacity) {}
/** 加入一筆元素;若已滿,則覆蓋最舊的一筆。 */
push(item) {}
/** 回傳目前所有元素,由最舊到最新。 */
toArray() {}
}寫完這幾行你就能先問:這個介面描述得簡短又完整嗎?若是,代表介面簡單、抽象乾淨——現在才動手寫主體。
紅旗:註解怎麼寫都又臭又長 → 抽象有問題
/**
* 加入一筆元素。若尚未滿就直接放到尾端;若已滿而且 overwrite 為
* true 就覆蓋最舊那筆並移動 head;若已滿而 overwrite 為 false 就
* 丟出例外,但如果同時 grow 為 true 則改為擴充容量再放入,擴充時
* 舊資料的順序要依 head 位置重新排列……
*/
push(item, overwrite, grow) {}註解必須寫得又長又複雜才描述得完,就是設計出問題的訊號:這個方法把太多決策塞進一個介面。這正是「難以描述(Hard to Describe)」的紅旗——註解在這裡扮演複雜性的煤礦坑金絲雀。修法不是把註解寫短,而是回頭把抽象拆乾淨,讓每個方法都能用一句簡單完整的話說清楚。
CAUTION
註解只有在它完整且清楚時才是複雜性的好指標。一段殘缺或晦澀的註解無法拿來衡量抽象的深淺——先把它寫到位,再用它來評斷設計。
🔑 Takeaways
- 寫註解的最佳時機是一開始,與程式碼同步;延後只會讓文件不寫、或寫成低品質的重複程式碼。
- 「程式碼還在變、之後要重寫」這個常見反對——真相多半是把寫文件當苦工而拖延;先寫介面註解反而讓抽象更早穩定,減少後續改動。
- 先寫介面該記錄的資訊,聚焦抽象與介面、不被實作分心,寫出來的註解自然多是「為什麼」而非重述。
- 註解是唯一能完整捕捉抽象的工具;描述不出簡短完整的註解 = 抽象有問題,這讓先寫註解成為一種設計工具。
- 這股「先寫、由文件驅動設計」的精神,與 TDD 的測試先行同源——都是在寫實作前先確立對外的樣貌。
No notes yet — jot your takeaways or Q&A here.