「能跑」不等於「能上線」,這是 AI coding 工具最常被誤解的一點。當同事把一份 Copilot 或 Cursor 生出來的 repo 丟過來、附一句「基本功能都好了,你幫忙收個尾」,真正的工作才剛開始。
半成品的問題不是「還沒寫完」,而是「看起來寫完了,但沒人知道邊界在哪」。工程師接手時,比起補功能,更花時間的其實是重建對這段程式的信任。
半成品常見的三種病灶
第一種是幻覺 API。AI 有時會呼叫不存在的函式、傳入不存在的參數,或引用某套件早已移除的 method。表面能跑,是因為被 mock 過、或走到 happy path 就 return 了。審查時要優先跑一次完整測試、把每個外部依賴的呼叫都對照官方文件驗過一遍。
第二種是過度抽象。AI 傾向於產出「看起來乾淨」的寫法:多一層 factory、多一個 interface、多一個 config 檔。單檔 demo 看不出問題,塞進既有專案卻會撞到既有慣例。重構原則很簡單——如果這個抽象沒有第二個使用者,先攤平。
第三種是沉默的錯誤處理。AI 生成的程式碼很喜歡把例外吞掉、或用 except Exception: pass 讓流程繼續。上線後這種寫法會讓 bug 難以追蹤,因為它們根本不會噴出來。
從 demo 到 production 的距離
假設一個典型情境:客戶要一個內部訂單匯入工具,AI 已經生了 CLI 骨架、能讀 CSV、也能呼叫後端 API。看起來完成度很高,但要上線至少還要處理:
- 輸入邊界:欄位缺失、編碼錯誤、重複資料時的行為
- 失敗恢復:中途斷線時,已經送出的資料要不要 rollback
- 可觀察性:log 有沒有帶足夠的 context 讓客服追查單筆
- 權限與稽核:誰執行、什麼時候、影響哪些資料
這幾項通常不會出現在 AI 的初版產出裡,因為它們不是「功能」而是「營運需求」。我們在報價時通常會提醒客戶,把 AI 生的原型丟給工程師的那一刻,剩下要做的工作往往比外觀看到的多。
# 示意:把 AI 生的 happy-path 呼叫包一層可觀察性
def import_row(row, ctx):
try:
validate(row) # 補上原本沒有的邊界檢查
result = api.submit(row, idempotency_key=ctx.key(row))
log.info("imported", row_id=row.id, trace=ctx.trace_id)
return result
except ValidationError as e:
log.warning("skip", row_id=row.id, reason=str(e))
raise
我們的觀察
AI 產出的程式碼像是一份很有信心的初稿,缺點不是它寫得爛,而是它不知道自己不知道什麼。工程師的價值從「把字打出來」轉向「補上情境、劃出邊界、留下可維運的痕跡」。我們的經驗法則是:接手 AI 半成品時,先花時間讀懂它「假設了什麼」,再決定哪些留、哪些砍。真正的產品化,從這一步才開始。
