提升程式碼的品質,就像培養品味一樣,不是與生俱來的天賦,而是透過一次又一次正確的取捨和自我要求所累積的成果。
程式碼的最終目標,是穩定地解決實際問題,而不是追求華麗技巧。
只要每天守住這四件事,你就能持續寫出比大多數人更好的程式碼:
在動手寫下任何一行程式碼前,請先停下來,用這幾個問題審視你的方向。
你真的需要寫這段程式/功能嗎?
許多新手或缺乏經驗的工程師,喜歡為了「未來可能的需求」而建立複雜的抽象層。
這樣過度設計會浪費時間,也讓程式碼變得更難理解與維護。
好的做法是專注於解決當下的實際問題,保持程式碼的簡單。
有沒有更簡單的方法?
在程式設計中,最簡單的方法往往也是最好的。
複雜的演算法、多層次的設計,雖然有時很必要,但更多時候只是緣木求魚。
在考慮任何「優化」或「精巧設計」之前,先確保最簡單、最直接的路徑已經被充分探索。
先求有,再求好;先砍枝葉,再談優化。
你的改動,會不會讓現有使用者出錯?
「向後相容性」(Backward Compatibility)是一條鐵律。
無論修改 API、函式介面或資料庫結構,都必須確保改動不會導致舊版本的使用者或服務崩潰。
如果必須做出破壞性的變更,正確的做法是新增一個新版本,而不是直接修改舊版本。
一個好的系統架構,可以依照這個順序來思考,將複雜問題拆解:
單一職責、原子化
將計算和 I/O (Input/Output) 操作分開,讓每個函式只做一件事,保持邏輯流暢。(Day 6, Day 16, Day 18)
介面最小化
對外只承諾最必要的介面,這樣內部重構時才能更有彈性。(Day 10)
分離設定
設定檔或參數從程式碼中抽離,由外層參數注入的方式管理,讓邏輯保持純粹和可預期。(Day 17)
這裡將文章中的主要原則,依據不同主題分門別類,幫助你快速理解和應用:
用名稱表達意圖
變數、函式、類別的命名,應該清楚傳達它們的用途和目的。(Day 1)
將複雜條件提取成具名函式
當判斷式太長時,將其包裝成一個描述性強的函式。(Day 14)
註解只解釋「為什麼」
好的程式碼本身就能說明「怎麼做」,註解應該用來解釋「為什麼要這麼做」的設計考量或取捨。(Day 21)
用資料結構簡化分支
透過修改資料結構,減少 if-else
或 switch
判斷,讓邏輯更直觀。(Day 2, Day 5)
消除重複邏輯
找出重複的程式碼模式,將其抽取成共用函式,避免複製貼上。(Day 12)
用常數取代魔術數字
將硬編碼的數字替換成有意義的常數名稱,提升程式碼可讀性。(Day 13)
組合優於繼承
別再為了點小功能就無腦繼承了。學會用「組合」來構建更有彈性的結構。(Day 15)
避免無意義的抽象層
如果一個抽象層沒有帶來實際好處,反而增加理解難度,就應該考慮移除。(Day 8)
將邊界判斷移出迴圈
將迴圈中的邊界條件或特殊處理提前處理,確保迴圈內部只處理核心邏輯。(Day 9)
計算與 I/O 分離
將純粹的資料計算邏輯,與檔案讀寫、資料庫操作等 I/O 邏輯徹底分開。(Day 11, Day 18)
設定外部化、簽名誠實
讓函式或方法接收它真正需要的參數,而不是依賴全域設定,這能讓函式行為更可預期。(Day 17)
最小承諾
對外部使用者來說,你的 API 應該越簡單越好,只暴露必要的功能。(Day 10)
新增而非修改
當需要變更介面時,優先考慮新增一個新版本,以維持向下相容。(Day 4)
讓錯誤順序變得不可能
透過好的介面設計,讓使用者無法用錯誤的順序呼叫函式,減少潛在的錯誤。(Day 29)
先驗證再處理
在程式入口處就對輸入進行型別、範圍等驗證,確保資料的正確性。(Day 22)
處理或拋出
對於預期內的錯誤,可以進行處理並回傳安全值;對於無法處理的錯誤,應該拋出例外。(Day 24)
別回傳 null
建議回傳安全的結構或空集合,例如:{ ok: boolean, value: any, error: string }
,而不是把 null
這個地雷丟給呼叫端。(Day 23)
以下是幾個你應該極力避免的寫法,它們是程式碼「壞味道」的主要來源:
為了「可能」的未來需求而堆疊抽象層級。(Day 3, Day 8)
使用布林參數讓一個函式做兩種不同的事,這會讓函式的意圖變得模糊。(Day 19)
null
將處理錯誤的責任轉嫁給呼叫端。(Day 23)
使用空的 catch
區塊或直接忽略錯誤。(Day 24)
使用者必須記住特定的呼叫順序才能使用,這種設計不直覺且容易出錯。(Day 29)
在迴圈中處理首尾或特殊邊界條件,這會讓迴圈本身變得複雜。(Day 9)
直接修改已被外部使用的介面,可能導致其他系統或模組出錯。(Day 4)
這是一份濃縮的行動指南,在你需要動手修改或發布程式碼時,作為你的檢查清單。
先補測試
確保你的修改不會破壞既有行為(Never break userspace)。
找到味道
尋找重複的程式碼、超過三行的縮排、冗長的條件判斷、隱藏的依賴或共享的狀態。
小步前進
先調整資料結構,接著簡化邏輯分支,最後才收斂介面。保持每一步都可回滾、可解釋。
清晰溝通
提交 PR (Pull Request) 時,附上動機、取捨、風險、回滾方式;必要時配合功能開關與監控。
輸入檢查
在入口處驗證型別、必填、範圍;失敗時的錯誤訊息要大聲而精準。(Day 22)
錯誤協議
可預期的失敗用回傳值(如 { ok, error, value }
),非預期的用例外。切記「處理或拋出」。(Day 24)
外部依賴
為外部呼叫設定 Timeout、僅對暫時性錯誤進行重試(Retry)、必要時準備降級方案(Fallback)。(Day 26)
資源管理
使用 try...finally
或是 Loan Pattern,確保取得的資源一定會被釋放。(Day 25)
併發一致性
優先使用資料庫原子操作或訊息佇列;確保對外操作具備冪等性,以便安全重試。(Day 27)
發布前,請逐一檢查這份清單,確保你的程式碼符合高品質的標準:
問題與解法
這次的改動,真的能解決一個實際存在的問題嗎?這是最簡單可行的解法嗎?
向後相容性
我的改動是否破壞了既有使用者的體驗?是否影響之前的功能?相容性策略是什麼?
資料結構
資料結構是否清晰地表達了資料關係與存取模式?
職責與介面
介面是否足夠精簡?每個函式是否單一職責?分離設定?
系統邊界
輸入驗證、錯誤協議、資源釋放、外部失敗策略是否都已妥善處理?
主要邏輯
迴圈內部是否只處理「正常情況」,而將邊界與特殊情況移出?
可觀測性
是否有足夠的測試與監控機制,能捕捉到潛在的錯誤,並支援快速回滾?
個人的習慣,可以透過團隊制度來放大,讓提升程式碼品質成為一種文化。
每日小重構
每天花 10 分鐘,主動尋找一處程式碼「壞味道」並進行重構。(Day 28)
有意義的 PR
在提交 Pull Request 時,清楚說明本次改動的動機、取捨、潛在風險與回滾方式。
事故後改進
每次事故發生後,產出具體的工程改進措施(更新檢查清單、調整策略、增加觀測指標),而不只是內部檢討。
消除程式碼的臭味,是一場沒有終點的修行。
它仰賴的不是天賦,而是一種持續自我要求的紀律。
用資料與簡單的結構讓程式碼自然變乾淨。
在系統邊界做好防護,把潛在風險降到最低。
把重構當作日常,持續保持程式碼的可讀與可測。
每天多一點堅持,程式碼就多一分清晰。
願我們都能成為程式碼好品味的工匠。