iT邦幫忙

2025 iThome 鐵人賽

DAY 30
0
Software Development

消除你程式碼的臭味系列 第 30

Day 30- 總結:從好品味到好架構的終極實踐清單

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20250903/20124462P1N8QjGguI.png

消除你程式碼的臭味 Day 30- 總結:從好品味到好架構的終極實踐清單

提升程式碼的品質,就像培養品味一樣,不是與生俱來的天賦,而是透過一次又一次正確的取捨和自我要求所累積的成果。

程式碼的最終目標,是穩定地解決實際問題,而不是追求華麗技巧。

只要每天守住這四件事,你就能持續寫出比大多數人更好的程式碼:

簡單(Simple)

穩定(Stable)

可讀(Readable)

可測(Testable)

https://ithelp.ithome.com.tw/upload/images/20251002/20124462TuV4Wp2MBp.png

Part 1:高層次心法 — 在動手之前

在動手寫下任何一行程式碼前,請先停下來,用這幾個問題審視你的方向。

三個關鍵思考

  1. 你真的需要寫這段程式/功能嗎?

    許多新手或缺乏經驗的工程師,喜歡為了「未來可能的需求」而建立複雜的抽象層。
    這樣過度設計會浪費時間,也讓程式碼變得更難理解與維護。
    好的做法是專注於解決當下的實際問題,保持程式碼的簡單。

  2. 有沒有更簡單的方法?

    在程式設計中,最簡單的方法往往也是最好的。
    複雜的演算法、多層次的設計,雖然有時很必要,但更多時候只是緣木求魚。
    在考慮任何「優化」或「精巧設計」之前,先確保最簡單、最直接的路徑已經被充分探索。
    先求有,再求好;先砍枝葉,再談優化。

  3. 你的改動,會不會讓現有使用者出錯?

    「向後相容性」(Backward Compatibility)是一條鐵律。
    無論修改 API、函式介面或資料庫結構,都必須確保改動不會導致舊版本的使用者或服務崩潰。
    如果必須做出破壞性的變更,正確的做法是新增一個新版本,而不是直接修改舊版本。

https://ithelp.ithome.com.tw/upload/images/20251002/20124462yhtfxgSHhI.png

程式碼架構:資料 > 結構 > 邏輯

一個好的系統架構,可以依照這個順序來思考,將複雜問題拆解:

  • 資料結構優先
    先設計能表達關係與存取模式的資料結構,這能讓你的程式碼邏輯更清晰。(Day 7, Day 20

  • 消滅特殊情況
    透過調整資料結構和流程,讓「例外」變成「常態」,簡化邏輯判斷。(Day 2, Day 5, Day 9

  • 單一職責、原子化
    將計算和 I/O (Input/Output) 操作分開,讓每個函式只做一件事,保持邏輯流暢。(Day 6, Day 16, Day 18

  • 介面最小化
    對外只承諾最必要的介面,這樣內部重構時才能更有彈性。(Day 10

  • 分離設定
    設定檔或參數從程式碼中抽離,由外層參數注入的方式管理,讓邏輯保持純粹和可預期。(Day 17

https://ithelp.ithome.com.tw/upload/images/20251002/201244621HnukJt8Gr.png

Part 2:主要原則 — 30天精華總覽

這裡將文章中的主要原則,依據不同主題分門別類,幫助你快速理解和應用:

命名與可讀性

  • 用名稱表達意圖
    變數、函式、類別的命名,應該清楚傳達它們的用途和目的。(Day 1

  • 將複雜條件提取成具名函式
    當判斷式太長時,將其包裝成一個描述性強的函式。(Day 14

  • 註解只解釋「為什麼」
    好的程式碼本身就能說明「怎麼做」,註解應該用來解釋「為什麼要這麼做」的設計考量或取捨。(Day 21

程式碼結構與抽象

  • 用資料結構簡化分支
    透過修改資料結構,減少 if-elseswitch 判斷,讓邏輯更直觀。(Day 2, Day 5

  • 消除重複邏輯
    找出重複的程式碼模式,將其抽取成共用函式,避免複製貼上。(Day 12

  • 用常數取代魔術數字
    將硬編碼的數字替換成有意義的常數名稱,提升程式碼可讀性。(Day 13

  • 組合優於繼承
    別再為了點小功能就無腦繼承了。學會用「組合」來構建更有彈性的結構。(Day 15

  • 避免無意義的抽象層
    如果一個抽象層沒有帶來實際好處,反而增加理解難度,就應該考慮移除。(Day 8

  • 將邊界判斷移出迴圈
    將迴圈中的邊界條件或特殊處理提前處理,確保迴圈內部只處理核心邏輯。(Day 9

職責與流程

  • 步驟原子化
    將複雜的流程拆解成一個個獨立且不可再分的步驟。(Day 6, Day 16

  • 計算與 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 25
  • 先消除共享狀態,再談鎖定
    鎖定機制很複雜且容易出錯,優先考慮設計無共享狀態的程式碼,或是利用原子操作。(Day 27
  • 對外操作的冪等性
    設計 API 時,應確保重複執行同一操作,其結果仍是相同的,這對於重試機制非常重要。

https://ithelp.ithome.com.tw/upload/images/20251002/20124462LNpNtPIwPo.png

Part 3:避開陷阱 — 常見的程式碼壞味道

以下是幾個你應該極力避免的寫法,它們是程式碼「壞味道」的主要來源:

過度設計

為了「可能」的未來需求而堆疊抽象層級。(Day 3, Day 8

布林參數

使用布林參數讓一個函式做兩種不同的事,這會讓函式的意圖變得模糊。(Day 19

回傳 null

將處理錯誤的責任轉嫁給呼叫端。(Day 23

吞掉錯誤

使用空的 catch 區塊或直接忽略錯誤。(Day 24

需要記憶順序的 API

使用者必須記住特定的呼叫順序才能使用,這種設計不直覺且容易出錯。(Day 29

在迴圈內處理邊界

在迴圈中處理首尾或特殊邊界條件,這會讓迴圈本身變得複雜。(Day 9

修改既有介面

直接修改已被外部使用的介面,可能導致其他系統或模組出錯。(Day 4

https://ithelp.ithome.com.tw/upload/images/20251002/20124462vKO5UhzozN.png

Part 4:終極實踐手冊 — 從重構到發布

這是一份濃縮的行動指南,在你需要動手修改或發布程式碼時,作為你的檢查清單。

Phase 1: 重構作戰:發現並清除壞味道

  1. 先補測試
    確保你的修改不會破壞既有行為(Never break userspace)。

  2. 找到味道
    尋找重複的程式碼、超過三行的縮排、冗長的條件判斷、隱藏的依賴或共享的狀態。

  3. 小步前進
    先調整資料結構,接著簡化邏輯分支,最後才收斂介面。保持每一步都可回滾、可解釋。

  4. 清晰溝通
    提交 PR (Pull Request) 時,附上動機、取捨、風險、回滾方式;必要時配合功能開關與監控。

Phase 2: 系統邊界:打造穩固的防線

  • 輸入檢查
    在入口處驗證型別、必填、範圍;失敗時的錯誤訊息要大聲而精準。(Day 22

  • 錯誤協議
    可預期的失敗用回傳值(如 { ok, error, value }),非預期的用例外。切記「處理或拋出」。(Day 24

  • 外部依賴
    為外部呼叫設定 Timeout、僅對暫時性錯誤進行重試(Retry)、必要時準備降級方案(Fallback)。(Day 26

  • 資源管理
    使用 try...finally 或是 Loan Pattern,確保取得的資源一定會被釋放。(Day 25

  • 併發一致性
    優先使用資料庫原子操作或訊息佇列;確保對外操作具備冪等性,以便安全重試。(Day 27

Phase 3: 發布前最終檢查

發布前,請逐一檢查這份清單,確保你的程式碼符合高品質的標準:

  1. 問題與解法
    這次的改動,真的能解決一個實際存在的問題嗎?這是最簡單可行的解法嗎?

  2. 向後相容性
    我的改動是否破壞了既有使用者的體驗?是否影響之前的功能?相容性策略是什麼?

  3. 資料結構
    資料結構是否清晰地表達了資料關係與存取模式?

  4. 職責與介面
    介面是否足夠精簡?每個函式是否單一職責?分離設定?

  5. 系統邊界
    輸入驗證、錯誤協議、資源釋放、外部失敗策略是否都已妥善處理?

  6. 主要邏輯
    迴圈內部是否只處理「正常情況」,而將邊界與特殊情況移出?

  7. 可觀測性
    是否有足夠的測試與監控機制,能捕捉到潛在的錯誤,並支援快速回滾?

https://ithelp.ithome.com.tw/upload/images/20251002/20124462LsXQLZZBzr.png

Part 5:從個人到團隊 — 把好品味制度化

個人的習慣,可以透過團隊制度來放大,讓提升程式碼品質成為一種文化。

  • 每日小重構
    每天花 10 分鐘,主動尋找一處程式碼「壞味道」並進行重構。(Day 28

  • 有意義的 PR
    在提交 Pull Request 時,清楚說明本次改動的動機、取捨、潛在風險回滾方式

  • 事故後改進
    每次事故發生後,產出具體的工程改進措施(更新檢查清單、調整策略、增加觀測指標),而不只是內部檢討。

https://ithelp.ithome.com.tw/upload/images/20251002/20124462YBw4MRgAiA.png

結語:成為具備程式碼品味的工匠

消除程式碼的臭味,是一場沒有終點的修行。
它仰賴的不是天賦,而是一種持續自我要求的紀律。

  • 用資料與簡單的結構讓程式碼自然變乾淨。

  • 在系統邊界做好防護,把潛在風險降到最低。

  • 把重構當作日常,持續保持程式碼的可讀與可測。

https://ithelp.ithome.com.tw/upload/images/20251002/20124462hXy4rx7ZiO.png

每天多一點堅持,程式碼就多一分清晰。
願我們都能成為程式碼好品味的工匠。


上一篇
Day 29-順序依賴:停止設計那種需要記住呼叫順序的 API
系列文
消除你程式碼的臭味30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言