反模式不是「明顯的錯誤」——它們看起來都很「專業」。策略模式是好東西,型別提示是好東西,docstring 也是好東西。但當它們在不該出現的時機出現,就變成了反模式。
本頁的四張速查卡,對應四個原則各自最常見的反模式。把它們貼在旁邊,每次收到 AI 輸出時快速對照。
| 原則 | 反模式 | 修正方式 |
|---|---|---|
| 編碼前思考 | 默默假設檔案格式、欄位、範圍 | 明確列出假設,請求釐清 |
| 簡潔優先 | 為單一折扣計算使用策略模式 | 一個函式就夠,直到實際需要複雜度 |
| 精準修改 | 修復錯誤時順便改引號、加型別提示 | 只修改修正問題所需的行 |
| 目標驅動 | 「我會檢視並改進程式碼」 | 「為錯誤 X 撰寫測試 → 讓它通過 → 驗證無回歸」 |
「過度複雜」的範例並非明顯錯誤——它們遵循了設計模式和最佳實務。問題在於時機:它們在需要之前就增加了複雜度,這會讓程式碼更難理解、引入更多錯誤、需要更長的實作時間、更難測試。
好的程式碼是簡潔解決今日問題的程式碼,而不是過早解決明日問題的程式碼。
步驟 1:看 diff 行數 — 如果改動超過 50 行,先問「這些都是必要的嗎?」
步驟 2:看新增的 import — 有沒有沒人要求的 import?(如 from abc import ABC)
步驟 3:看新增的類別/函式 — 有沒有沒人要求的抽象?(如 Strategy Pattern)
步驟 4:看註解與 docstring — 有沒有「改進」了現有的註解?
步驟 5:看引號與空白 — 有沒有改變了現有的風格?
步驟 6:問「資深工程師會說這太複雜嗎?」
class UploadService:
"""Handles file uploads with validation, chunking,
progress reporting, and retry with backoff."""
def __init__(self, storage, validator=None, reporter=None):
self.storage = storage
self.validator = validator or DefaultValidator()
self.reporter = reporter or NullReporter()
def upload(self, path, chunk_size=1MB, retries=3, backoff=1.5):
for chunk in read_chunks(path, chunk_size):
if not self.validator.validate(chunk): raise ...
self.reporter.progress(len(chunk))
for attempt in range(retries):
try: self.storage.put(chunk); break
except: time.sleep(backoff * attempt)
return True
步驟 1 看行數:60+ 行,含 5 個方法、3 個可注入物件 → 對照「簡潔優先」:需求只要「把檔案存起來」。 → 質疑點:chunking、retry、progress、validator 誰要求的? 步驟 2 看 import:有抽象類別、預設實作、backoff 邏輯 → 對照「精準修改」:這些是「額外元件」不是「必要元件」。 步驟 3 看新增類別:UploadService + DefaultValidator + NullReporter → 三個抽象只有一個是被需求的。 步驟 4 看註解/docstring:完整英文 docstring → 語氣正確、內容工整——但「專業的形狀」≠「必要的內容」。 步驟 5 看風格:class 風格與專案其他檔案一致嗎? → 這份專案是 module-level function 風格——class 本身就是風格偏差。 步驟 6 資深工程師測試:他會說太複雜嗎?→ 會,100%。 最終判定:四項反模式全中(過度工程、範圍蔓延、風格偏差、 模糊需求)。但因為它「長得專業」,60% 的 review 會放行。
這是速查頁最核心、也最反直覺的一課:反模式不是「寫得差」的程式碼,而是「寫得太好」的程式碼。策略模式是教科書級好設計、型別提示是現代最佳實務、docstring 是工程紀律——這些東西單獨出現都是加分項。問題永遠在時機與必要性,不在品質。
模型的訓練資料讓它極度擅長「複製權威的外觀」:開源專案裡,設計模式密集、註解齊全、錯誤處理完整的程式碼往往被標記為高品質(因為 review 過、被 merge 了)。模型學到的是「長成這樣 = 高品質」,於是它在高風險、高要求的需求上,會優先複製這種形狀——不是因為需求需要,而是因為訓練資料獎勵它。
自我強化的迴圈:過度設計的程式碼因為「看起來穩健」而更容易通過 review → 更多「看起來穩健」的程式碼被 merge → 模型下次更確信「這是對的」。這就是為什麼速查表的存在很重要:它是唯一能打破這個迴圈的、來自需求側的檢查。
開發者(或模型)宣稱「我遵守了簡潔優先:只寫了被要求的兩個函式」。但仔細看——這兩個函式各自接收一個 options dict,裡面有 6 個從未使用的鍵;還有一個 module 層級的 DEFAULT_OPTIONS 常數。表面上沒有「多餘的 class」,但「多餘的參數化」就是被縮小尺寸的過度工程。反模式的關鍵不是形式(class 或 dict),而是猜測性內容的總量——它只是換了一個比較不顯眼的外殼。
| 症狀 | 可能原因 | 解決方案 |
|---|---|---|
| review 只檢查「有沒有 bug」,忽略「是不是需求」 | 注意力稀缺,專業外殼誘導審計方向 | 把審計第一題改成「每一行能否追溯到需求?」 |
| 「看起來穩定」的輸出被快速放行 | 以「外觀品質」取代「需求匹配」作為通過標準 | 用速查表六步流程,而非整體印象做判斷 |
| 團隊對「何謂過度設計」沒有共識 | 缺少共享的簡潔定義 | 訂一個可操作的標準:資深工程師 30 秒內講得出每個物件的用途 |
| 反模式換成小包裝(options dict、預設參數)就躲過檢查 | 速查表只看「形式」不看「猜測性內容的總量」 | 檢查改為:數一下「未被需求的決策」有幾個,而不是數 class 有幾個 |
| 幾次放行後,團隊對反模式的警覺降低 | 基準偏移——看慣了過度設計,見怪不怪 | 每週挑一份合規的最小 diff 當基準,重新校準眼力 |