Vibe-Coding 陷阱:為什麼 AI 驅動的開發速度是技術債的加速賽

在專案的早期階段,AI 輔助編碼感覺就像擁有一種超能力。你輸入提示詞(prompt)要求一個功能,幾分鐘後它就運作了。這種現象通常被稱為「vibe-coding」,它創造了一種誘人的生產力幻覺,讓想法與可運作原型之間的距離縮短到幾乎為零。然而,正如一位開發者在構建 k10s(一個感知 GPU 的 Kubernetes 儀表板)時所發現的,這種速度伴隨著隱藏且會產生複利成本的代價。

在利用 Claude 進行了七個月的高速功能開發後,作者發現其程式碼庫已變成了一堆難以維護的「義大利麵條碼」殘骸。該專案並非因為 AI 無法編寫功能而失敗,而是因為 AI 寫的是功能,而非架構。當開發者最終停止輸入提示詞並實際閱讀程式碼時,他們發現了一個 1,690 行的 model.go 檔案,其中包含一個巨大的「上帝物件」(god object),管理著從 UI 元件、K8s 客戶端到滑鼠處理和導航歷史的所有內容。

程式碼庫崩潰的解剖學

k10s 的崩潰可以作為一個案例研究,說明 AI 驅動的開發如何悄無聲息地侵蝕軟體品質。作者指出了五個主要的「殘骸教條」,解釋了程式碼庫是如何自我吞噬的。

1. 以功能為中心 vs. 架構完整性

AI 會針對即時的提示詞進行優化。當被要求添加一個「艦隊視圖」(fleet view)時,AI 會在孤立狀態下完美地實現它。然而,它缺乏對系統長期健康的整體理解。在 k10s 中,這導致了散佈在程式碼各處的「特殊情況處理」(special-casing)邏輯。為了防止一個視圖的數據流向另一個視圖,作者發現檔案中存在九處手動的 nil 賦值——這些脆弱的清理行,一旦遺漏,就會導致 UI 中出現「幽靈數據」。

2. 上帝物件的引力

由於 AI 尋求滿足提示詞的最短路徑,它自然會傾向於向現有的 struct 結構體中添加欄位,而不是設計新的抽象。這導致了條件邏輯的噩夢。例如,TUI 中的 s 鍵必須根據當前視圖執行三個完全不同的動作,所有這些動作都在一個單一、扁平的 switch 陳述式中處理,使用的是字串比較而非型別化的分派(typed dispatch)。

3. 速度的幻覺

高速度可以掩蓋複雜度預算的縮減。作者指出,因為功能開發感覺是「免費」的,他們擴展了範圍,從一個小眾的 GPU 工具變成了一個通用的 Kubernetes TUI。

"Vibe-coding 使你覺得自己擁有無限的實作預算。你其實沒有。你擁有的是無限的程式碼行數預算……但你擁有的是與以往相同的有限複雜度預算。"

4. 位置數據的定時炸彈

為了快速將數據顯示在螢幕上,AI 經常使用扁平化陣列 ([]string) 而非型別化的 struct 結構體。這意味著欄位身份純粹是基於位置的(例如,row[3] 是 "Alloc")。只要設定檔中的欄位順序發生一次變動,就會悄無聲息地破壞應用程式中的每個排序函數和條件渲染,因為編譯器無法捕捉到基於索引的字串切片錯誤。

5. 並行處理的差距

AI 經常建議實現功能的捷徑,這通常涉及在閉包(closures)或 goroutines 中修改狀態。在 k10s 中,這導致了教科書式的數據競爭(data races),即背景更新在主迴圈渲染時修改了 model,導致了間歇性、難以除錯的 UI 損壞。

建立護欄:CLAUDE.md 策略

為了防止在重寫過程中再次發生這種崩潰,作者建議將架構的重擔重新交還給人類。關鍵在於於專案層級的指令檔案(例如 CLAUDE.mdagents.md)中定義嚴格的「架構不變量」(Architecture Invariants),讓 AI 在每次輸入提示詞之前都必須閱讀它們。

建議的護欄:

  • 狀態所有權: 禁止向全域的 App/Model struct 結構體中添加視圖特定的狀態。每個視圖必須是一個實作了共同介面的獨立 struct 結構體。
  • 數據表示: 禁止使用位置陣列來表示結構化數據。要求在最終渲染呼叫之前使用型別化的 struct 結構體,以實現「讓不可能的狀態成為不可能」。
  • 並行處理規則: 強制要求背景任務絕不能直接修改 UI 狀態;它們必須將型別化的訊息傳回主事件迴圈。
  • 範圍邊界: 明確定義該工具「不是」為誰設計的,以防止由 AI 生成的便利性所驅動的功能蔓延(feature creep)。

AI 時代的人類元素

社群對此經驗的反應突顯了開發者如何看待 LLM 的日益分歧。有些人認為作者的失敗在於缺乏監督,指出「軟體工程的難點從來不在於編寫程式碼,」而是在於設計與驗證。

其他人則認為,「vibe-coding」的經驗是經典專案管理失敗的加速賽——讓人以慘痛的教訓學到,規格書與設計文件並非官僚主義的障礙,而是管理複雜度的必要工具。

最終,教訓在於,雖然 AI 可以將產生程式碼行數的邊際成本降低到零,但它並不會降低複雜度的成本。開發者決定用 Rust 重寫該工具——一種他們認為可以更有效地「操控」的語言——這強調了一個至關重要的點:在 AI 時代,最有價值的技能並非提示詞工程,而是能夠在生成的程式碼變成系統的基礎之前,就識別出它是「垃圾」的能力。

Sources