AI 時代,軟體基本功比以往都重要
在 AI 時代,軟體基本功比以往都重要。最近 Matt Pocock 在 AI Engineer Europe 的一場演講很熱,標題是〈Why Software Fundamentals Matter More Than Ever〉。
我滿認同他的觀點。AI 確實讓寫 code 的速度變快了,但它沒有讓軟體工程的基本功變得不重要。剛好相反,基本功會決定你能不能將 AI 用得好:能不能讓 AI 理解你的系統、能不能改對地方,以及它是在幫你加速,還是在幫你更快累積技術債。
這篇文章主要會整理 Matt 這場演講的內容與重點。
一、Specs-to-code 的問題
Matt 一開始先評論了最近很流行的 specs-to-code。
Specs-to-code 的核心想法是:先把需求和規格寫清楚,然後讓 AI 根據這些規格生成對應的程式碼。如果結果不對,就回頭修改規格,再讓 AI 重新生成一次。
聽起來很合理,但 Matt 認為,這種做法很容易走向一個問題:你每次都只是在改 spec、重新生成 code,卻沒有真正理解和維護 codebase 本身。反覆幾次之後,產出的程式碼可能會越來越難改,最後變成一團混亂。

所以他開始思考一件事:如果要讓 LLM 幫我們寫 code,我們是不是也要讓它理解什麼是爛 codebase、什麼是好的 codebase?
那麼,什麼是爛程式碼?
Matt 引用《A Philosophy of Software Design》對 complexity 的定義:
複雜性指的是軟體系統中,讓人難以理解和修改的結構問題。
簡單來說,爛 codebase 就是不好改的 codebase;好的 codebase 則應該是容易理解、容易修改的。
他也用《The Pragmatic Programmer》裡的 software entropy 來解釋這件事。Software entropy 指的是軟體系統會隨著時間越來越混亂,如果沒有人持續維護設計和結構,系統就會逐漸失控。
Specs-to-code 的問題就在這裡。它很容易讓人以為自己可以不用看 code:反正 spec 改一改,AI 再跑一次就好。但這樣做不是在投資系統設計,剛好相反,你是在系統性地放棄系統設計,把每一次修改都當成局部問題處理,而不再維護整體結構。
如果你不關心 codebase 的品質,AI 只會更快把 software entropy 推高。
二、Code is cheap? 不,爛 code 更貴了
Specs-to-code 背後其實有一個常見想法:
Code is cheap.
也就是說,既然 AI 可以快速生成程式碼,那 code 本身就變便宜了。
但 Matt 認為這個觀點不對。真正昂貴的不是「產生 code」這件事,而是 爛 code 造成的後續成本。
如果你的 codebase 難以理解、難以修改,那你其實很難享受到 AI 帶來的生產力紅利。因為 AI 不是在真空中寫 code,它是在你的既有系統裡工作。當系統本身混亂,AI 也會被混亂拖住。
相反地,AI 在好的 codebase 裡會表現得非常好。當命名清楚、邊界明確、測試完善、模組設計合理,AI 比較容易理解上下文,也比較容易做出正確修改。
這也是 Matt 這場演講的核心觀點:
AI 沒有讓軟體工程基本功變得不重要,而是讓它變得比以前更重要。
換句話說,AI 可以讓你更快產生 code,但如果 codebase 本身難以維護,這些產出只會更快累積成技術債。真正重要的不是「能不能產生更多 code」,而是 codebase 能不能持續被理解、修改和演進。
三、AI coding 的失敗模式:症狀與解法
接下來進入實戰環節。
Matt 在演講裡整理了幾種 AI coding 常見的失敗模式。這些問題表面上看起來像是「AI 不夠聰明」或「prompt 不夠好」,但 Matt 的重點是:很多時候,真正缺的是被 AI 放大的軟體基本功。
下面依序整理這幾個 failure modes,以及 Matt 對應提出的解法。
Failure Mode 1:AI 沒有照我的想法做 → Design concept
第一個常見症狀是:你覺得自己已經講清楚了,但 AI 做出來的東西跟你想像的不一樣,甚至完全偏掉。
Matt 引用《The Pragmatic Programmer》作者 David Thomas 和 Andrew Hunt 的一句話:
No-one knows exactly what they want.
沒有人能完全清楚知道自己想要什麼。
很多時候,你以為自己知道要做什麼,但其實腦中還有很多沒有說出來的前提、限制、取捨和邊界。人類同事可能可以補完這些空白,但 AI 不一定知道。
Matt 接著回到 Frederick Brooks 在《The Design of Design》提到的 design concept:多人協作時,大家需要共享一個對設計對象的理解。它比較像是一種共同的方向感:大家知道要解決什麼問題、為什麼這樣做,以及哪些選項不該做。
套到 AI coding 裡,問題就變成:你和 AI 之間還沒有共享同一個 design concept。
所以 Matt 的做法不是一開始就叫 AI 寫 code,而是先讓 AI 反過來問問題。他做了一個 skill 叫 /grill-me,意思就是讓 AI 不斷追問需求細節,直到雙方對問題有足夠接近的理解。
它會問需求、限制、edge case、設計選項,以及每個決策之間的依賴關係。等這些問題被釐清之後,再進入 implementation。
Matt 的觀察是,很多時候 AI 寫錯不是因為它不會寫 code,而是你們根本沒有共享同一個 design concept。直接叫 AI 寫,只是把模糊需求變成一堆看起來完整的 code;後面要修這些錯誤,通常會花更多時間,也讓 codebase 更亂。
他也提到,/grill-me 在這件事上比單純進入 Plan Mode 更有用。Plan Mode 會迫不及待想趕快開工,但 `/grill-me` 的重點是先建立設計理解,再開始規劃和實作。
Tip:Before you code, reach a shared design concept.
Failure Mode 2:AI 太 verbose、雞同鴨講 → Ubiquitous language
第二個症狀是:AI 太 verbose,或是跟你雞同鴨講。它可能用一堆術語解釋一個簡單功能,也可能明明語法正確,卻完全沒有講到你的 domain 重點。
Matt 說,這件事其實和開發者跟 domain expert 合作時遇到的問題很像。
假設你要和領域專家一起開發某個專業領域的系統,但你連那些專業術語是什麼意思都不知道,那你們之間就會出現溝通落差。對方說的是一套語言,你腦中理解的是另一套語言,最後寫出來的 code 也可能和真正的 domain 意義對不上。
Matt 把這件事連到 Domain-Driven Design 裡的關鍵概念:ubiquitous language。
Ubiquitous language 指的是讓開發者、domain expert、文件和 code 都使用一致的語言。這在人類團隊裡本來就重要,在 AI coding 裡更重要,因為 AI 很依賴上下文。如果上下文裡的詞不一致,AI 就會開始猜,最後可能產生語法正確、型別正確,但 domain 語意錯誤的 code。
所以一致的命名不是小事。命名越清楚,AI 越不用浪費 context 和 token 去猜你的 domain。
Matt 的做法是建立一份共同語言文件,也就是把專案中的專業術語整理成 Markdown。這份文件可以描述每個詞的意思、使用情境,以及哪些詞不應該混用。
他也做了一個 skill 叫 /ubiquitous-language,用來掃描 codebase,自動識別專案裡的專業術語,並生成一份 Markdown 文件。這份文件可以給 AI 當上下文,也方便人類自己查閱。
這樣做的目的不是多寫一份文件,而是讓人、文件、code 和 AI 都使用同一套語言。當語言一致,AI 的規劃會更聚焦,產出的結果也比較容易符合真正的 domain 需求。
Tip:Create a shared language with AI.
Failure Mode 3 + 4:AI 寫出來不能跑、一次做太多 → Feedback loop
第三個症狀是:你已經和 AI 達成共識了,但它寫出來的 code 不能跑。
第四個症狀則是:AI 一次做太多。
這兩個問題其實都和 feedback loop 有關。你需要讓 AI 很快知道自己做對還是做錯;如果 feedback 太慢,AI 就很容易一路往錯的方向跑下去。
Matt 引用《The Pragmatic Programmer》裡的比喻:don’t outrun your headlights。意思是,不要跑得比車燈照得到的範圍還快。
放到 AI coding 裡,就是:你的 feedback 速度,就是你的開發速度上限。
AI 常見的問題是一次做太多:先產生一大堆 code,最後才想起來要型別檢查,甚至最後才補測試。等到發現問題時,它已經跑太遠了,你也很難判斷到底是哪一步開始壞掉。
Matt 在這裡提出的解法是 TDD。
這裡的 TDD 不是教條式地說「一定要先寫測試才是好工程師」,而是因為 TDD 很適合限制 AI 的步伐:
- 先定義行為
- 讓 AI 寫 implementation
- 跑測試
- 根據結果修正
- 再繼續下一步
這樣 AI 會在小範圍內小步前進,而不是一次改一大坨,最後才發現不知道哪裡壞掉。
但 Matt 也提醒,測試不是你想寫就一定好寫。有些 codebase 天生就難測:邏輯到處散、副作用混在一起、module 邊界不清楚,一個小功能會牽動很多地方。這種 codebase 不只人難測,AI 也難測。
所以這又回到 codebase 品質:好的 codebase 更容易測試,也能提供更快、更可靠的 feedback loop。
Tip:Use TDD to keep AI inside the feedback loop.
Failure Mode 5:AI 看不懂我的 codebase → Deep modules
第五個症狀是:AI 在你的 codebase 裡迷路。
如果 codebase 裡都是一堆很淺、很碎、互相依賴的 modules,AI 要理解一個功能時,就必須到處跳檔案:看 helper、看 util、追 service、再追其他 mapping。最後它可能讀了很多東西,卻還是不知道真正的責任邊界在哪。
這種 shallow modules 就像沒組裝完的 IKEA 家具被拆散在四個房間——每塊板子都有用,你必須跑遍整個家,才知道哪個螺絲屬於哪塊。
Matt 在這裡引用 John Ousterhout 在《A Philosophy of Software Design》提出的 deep modules。
Deep module 的意思是:module 內部可以處理很多複雜邏輯,但對外 interface 要簡單。
這對人有幫助,對 AI 也有幫助。因為 AI 不需要一開始就理解整個世界,它可以先看 interface:知道輸入是什麼、輸出是什麼、這個 module 承擔什麼責任、哪些行為被測試保護,然後再進去改 implementation。
Matt 用微波爐來比喻 deep module:外面只有簡單的按鈕,但裡面可以有很複雜的機制。使用者不需要理解內部細節,也能正確使用它。
這就是 deep module 的價值:把複雜度封裝在清楚的邊界裡,讓人和 AI 都能透過簡單的 interface 理解系統。
Tip:Use deep modules to make your codebase easier for AI to understand.
Failure Mode 6:My Brain Hurts
最後一個症狀是:My brain hurts。
當系統缺少清楚的邊界,複雜度又被分散在許多細碎的 module 裡時,每一次修改都需要理解大量 implementation details:資料從哪裡來、狀態在哪裡改變、哪些 module 會被影響、哪些行為可能因此壞掉。久而久之,不只是 AI 會迷路,人類工程師也會被這些細節壓垮。
在這種情況下,把需求直接丟給 AI,通常不會真的降低複雜度,只是把複雜度延後爆炸。AI 可能可以幫你寫出一段能跑的 code,但如果你沒有先定義好 interface、責任邊界和測試保護,它也很容易在錯的地方修改,或把更多細節塞進已經混亂的系統裡。
Matt 在這裡提出的方向是:設計 interface,把 implementation 交給 AI。
也就是說,人類工程師重點不是追每一行 implementation 細節,而是更關鍵的工程判斷:整體系統設計、邊界該怎麼切、對外介面該怎麼設計、哪些行為需要被測試保護、domain language 是否一致。
Tip:Design the interface, delegate the implementation.
小結:Failure Modes 與對應解法
| Failure Mode | 表面症狀 | 真正的問題 | 對應的基本功 / 方法 |
|---|---|---|---|
| 1 | AI 沒有照我的想法做 | 缺少共享的設計理解 | 先建立 shared design concept。skill: /grill-me |
| 2 | AI 太 verbose、雞同鴨講 | 缺少共同的 domain 語言 | 建立 ubiquitous language。 skill: /ubiquitous-language |
| 3 + 4 | AI 寫出來不能跑、一次做太多 | Feedback loop 太慢 | 用 TDD 讓 AI 小步前進 |
| 5 | AI 看不懂我的 codebase | 模組太淺、太碎,責任邊界不清 | 用 deep modules 改善架構 |
| 6 | My brain hurts | 人和 AI 都被太多細節壓垮 | 設計 interface,把 implementation 交給 AI |
結論:
AI 是一個很強的 tactical programmer,像是一名前線士兵,負責實際的 code 開發與修改。
而人類工程師的角色,不能只停留在 tactical 層次。我們更需要做 strategic programmer,也就是站在更高的位置思考全局。這個 tactical / strategic 的對比,也是 Ousterhout 在《A Philosophy of Software Design》裡提到的概念。
AI 可以幫我們加速 implementation,但人類要負責更關鍵的工程判斷:系統該怎麼設計、module 邊界在哪、什麼該抽象、什麼不該抽象、哪些命名會誤導未來的人和 AI、哪些測試真的能保護系統。
AI 可以讓你更快產生 code,但它也會讓錯誤的設計、模糊的命名、破碎的邊界和缺乏測試的代價更快浮現。當 codebase 本身難以理解和修改,AI 的速度不一定會帶來生產力,反而可能只是更快累積技術債。
而要做出這些判斷,靠的是扎實的 software fundamentals。
這也是 Matt 這場演講真正想強調的地方:在 AI 時代,軟體基本功比以往都重要。
