CleanCode 無瑕的程式碼 (下篇)

無瑕的程式碼 CleanCode

作者:Robert C. Martin
譯者:戴于晉、博碩文化
審校:陳錦輝
出版社:博碩文化

CleanCode 無瑕的程式碼 (上篇)


接續著前一篇,無瑕的程式碼前半段著重於程式碼的季節面,下半段討論更多大方向的目標,討論規劃系統架構、測試、程式碼橋接規劃等,不再是單一個程式設計師自己撰寫程式碼的世界觀,書中講述更多的團隊運作方式和長期規劃方面的觀念。

從更高的視角看整潔程式碼

除了命名正確之外,個人的經驗是當系統成長到一定的規模時,開發者會不願意去面對舊有的程式碼,就算命名相當正確、易讀易懂,但修改或新增功能總是需要大動干戈,綁手綁腳的東修西改。同時因為這個原因,常常讓我放棄重構的念頭,有時甚至是直接刪掉某個部份的程式碼重來。想當然爾,這種錯誤只會日積月累,屆時將會進入完全無法挽回的地步。及早發現、儘早治療。

畫圈圈耍自閉,跟其他人劃清界線

邊界,是系統設計中一個簡單易學且效益極高的設計重點。以 Node.js 來說,會頻繁地使用到第三方套件,而更新第三方套件、客戶更改需求、或抽換相依套件,以上這些動作相當的困難且風險極高。小幅度的更改讓系統看似沒有問題,但完全不知道啥時會爆炸出個莫名其妙的 Bug。當有大範圍需要修改時更是可怕,個人經歷是改下去以後,東邊爆炸西邊起火,搞了老半天系統還沒正常過,好不容易穩定下來後來仍然令人疑心疑鬼。

邊界最單純的作法就是在引用第三方套件時,包裝一次成為自己的介面,這個包裝的地方,即可視為所謂的程式碼邊界。爾後開發統一引用包裝過後的介面,而邊界函數橋接的第三方套件則是外部程式碼,明確的劃清雙方的界線。透過定義自己程式的邊界,明確的定義自己的維護案範圍,並同時降低了對外的耦合,避免雙方程式碼高度的交織。並針對使用中的介面撰寫一套測試,確保對外的依賴運作正常,在升級版本時即可大幅地減輕痛苦。而升級時如果對方API有修改,透過邊界一次性的修改即可,不須逐一在專案中追逐散落在各個角落的引用。

另一個角度,開發時也可以利用邊界,在其他API尚未完成時進行開發,完成後再橋接邊界另一半的程式碼。

測試程式碼是一種品質保證

無論是否是利用 TDD ( Test-driven Development,測試驅動開發),測試仍然一樣的重要。除了上述的邊界測試、減輕修改程式碼負擔之外,測試更有心理層面的效果:給予開發人員修改的信心。讓我們在修改程式碼後,確認功能仍然正常運作。測試分為相當多個層級,在許多書中有不同的論點。個人的經驗是至少要對系統的最核心功能有一套測試保護,並模擬使用者整個操作的流程,這樣能在最簡單的情況下,保證著系統還算是正常的運作,不至於整個系統崩潰了卻無人知曉。以 EC 來說,最核心就是結帳功能,如果能時時確保功能正常,是完全沒有爭議的好事情。

如果讓你的測試程式碼腐敗,那麼你的產品程式碼也會跟著腐敗。請保持你的測試整潔。

雖然說測試的速度並非首要,但會影響到開發人員的效率或是心態,因此頻繁、簡便、快速的測試仍然是很重要的,細心的維護您的測試程式碼:

多弄幾次整理好

重構,基於有測試保護功能正常,開發者能放心的重構程式碼,將程式碼重構成理想的結構,更加明確的表態功能作為,甚至是修改效能、強化防呆等等。

在許多書中都提及相同的開發經驗:請立即重構,不要擱置。擱置重構只會讓這個問題繼續蔓延下去,放著放著忘記了,之後就沒人管了。這個污痕將會永留存。立即重構也利於開發速度,開發者也對相關狀況最清楚的時候重構程式碼,成本也相對低的。

這些都是常見的致命陋習

書中在最後的篇幅大幅度的探討常見的錯誤,諸如:

不適當的註解

程式碼修改後沒有跟著修改註解、多餘的廢話等等。註解應該保留給技術性紀錄。而冗餘的註解程式碼,就刪掉他吧!我們都有 Git 了!

亂來的函數

函數在沒有特地請求的情況下”順便”回傳了其他資料,這會造成其他開發者的困擾,只因為我們在其他邏輯上將這兩個功能結合在一起,正確的做法是徹底的切割功能。

旗標參數,我之前就常犯這個錯誤,在函數的傳入參數中加上類似 flag:boolean 的參數,讓函數能根據 flag 變化,錯誤相當明顯:這個函數做了兩件事情,pure function 才是王道啊。函數中如果真的有複數的操作,請明確地反映在函數名稱上面,雖然說函數上方能撰寫說明註解,但更多的狀況是開發者在其他處看到類似的功能,複製後就直接嘗試使用。

請用大眾化的思考方式思考

最少驚奇原則,當然系統中會有很多巧妙的設計,但撰寫程式碼時讓其他程式設計師訝異不是件好事情,無意義的多回傳東西,多修改其他地方的值或是參數等等,都是屬於不必要的功能,甚至造成無意義的困擾。當然也不要無意義的炫技,讓人看得懂的程式碼才是最好的。

減少整個專案中重複的程式碼,嘗試利用已經擁有的功能,或是在已經擁有的套件中擴充功能,有時閱讀別人的程式碼很令人煩躁,但事實這就是這樣,屢次的堆疊新功能和新程式碼上去,雖然有明顯的短期收益,但對整體專案的健康影響是相當大。

找到重複的地方,並移除這些重複。

而程式碼的排序、編排方式也是相同的,專案中使用一樣的 Coding Style 算是相當基本的要求。命名也是相同的邏輯,temp, tmp 在普遍的認知中就是暫存用的資料,如果是使用者資料請用 user, users 等等,濫用無意義的命名會帶來更多的痛苦。

1
2
3
4
// 有講跟沒講一樣的 function name
function dataOperator(data) {
// ...
}

在許多複雜的結構設計中,遵循設計模式是個不錯的選擇,簡單地表明是工廠模式、橋接模式等,會為閱讀者帶來更明確的方向和視野。

不要偷懶

偷懶是一切問題的開始,正確地表達程式碼語意,在短期上面看似工作效率更低,但多為未來的自己著想,不要偷懶不撰寫測試程式碼或是文件,這些東西在往後會以更高的成本出現,趁早完成對大家都有好處。如果 PM 或是老闆不同意,嘗試說服他吧!同時,當我們在修補 Bug 時,也可以試著為 Bug 補上對應的測試,避免後人犯錯,同時詮釋了自己這次修改的程式碼原因。

結語

在無瑕的程式碼中,有一部分的章節討論多執行緒的開發與設計,在這部分的內容我並沒有寫進心得中,主因是我撰寫這個部落格時技術核心是選擇:Node.js。我仍然相當推薦書中的內容,嘗試透過多執行語言的開發問題來反思非同步開發,是相當有助益的。

而整本書帶給我的,最多的是開發上的思維整理,會突然想到說:啊!原來就是因為當初那樣寫才…。有很多錯誤都是我在學生時期犯過的,在工作之後有嘗試著改變,閱讀此書之後大大的幫助我整理,讓思路更明確且清澈,同時也明確的感受到前人犯下的錯誤,期許自己能在程式開發生涯中,更自律的要求自己。

分享到