前陣子我看了 Matt Pocock 的演講:"Software Fundamentals Matter More Than Ever" — Matt Pocock
六大 AI 典型失敗模式 - 從 AI 時代看軟體工程師的價值
ThisWeb
資深前端工程師
發佈/更新於
免費訂閱電子報!
和 2000+ 工程師一起學習軟體、AI 開發技巧,每週一收穫 1 篇技術內容、1 段職涯分享、1 個最新資訊!
ThisWeb
資深前端工程師
發佈/更新於
和 2000+ 工程師一起學習軟體、AI 開發技巧,每週一收穫 1 篇技術內容、1 段職涯分享、1 個最新資訊!
前陣子我看了 Matt Pocock 的演講:"Software Fundamentals Matter More Than Ever" — Matt Pocock
Matt Pocock 在這場演講裡提出觀點:在 AI 時代下,你過去積累的軟體工程基本功,會比以往更加重要。
因為 AI 在一個好的 Codebase 裡面,表現會比糟糕的 Codebase 好上許多。因此,如何打造好的 codebase 的能力,也就是軟體工程的基本功,在 AI 時代的價值反而更高了。
我覺得這有點像打地基的感覺,程式架構規劃的好,AI 就能如魚得水般開發。
Matt Pocock 也用 Specs to Code 的方法論來說明這件事情。
Spec to Code 的概念是:我們寫規格文件,讓 AI 把規格轉成程式碼,如果出了問題,我們也不用看程式碼,只需要把規格改好,讓 AI 繼續修就好。
但 Matt 說每跑一次,程式碼就越來越爛,最後就剩一堆垃圾。
我最近的體悟也是這樣,我花了大把的時間把規格寫好,甚至把我認為好的 Coding Style 定義好,也在每次完成一個功能,讓不同的 AI Model 各自 Reivew,雖然程式能運作,但 Codebase 卻充斥著相同功能不同名稱、或是小到沒必要抽離的 Function,甚至常常出現每一個 function 就切成一個檔案的情況,檔案的位置也很分散,不是把相同 Domain 的 Function 聚集在一起。
這些要求我已經在 AGENTS.md 寫好了,但出來的結果仍然很混亂。
這導致我遇到問題要 Review 或找 Bug 時,需要花了一堆時間在一堆 file 切來切去。
會發生這種問題,Matt 説是因為這樣的開發方式違背了《The Pragmatic Programmer》裡提到的「軟體熵」概念,意思是如果我們每一次只想著眼前的改動、不考慮整體設計,系統就會逐漸走向崩潰。
所以 Matt 也不認同 「Code is cheap」,因為爛的程式碼會變得非常貴,不但難以人工維護,AI 開發起來也會花費更多的 Token,甚至功能越做越偏。
Matt 在經過大量的實務開發後,總結出 6 個 AI 典型的失敗模式以及對應的解法。
你心裡有個清楚的想法,但 AI 做出來的東西是另一回事。這個問題的本質,其實和你跟另一個人合作時會遇到的問題一樣。
Matt 引用了《The Design of Design》裡提到「設計概念(Design Concept)」這個詞,指的是合作者之間的那個很模糊,但真實存在的背後理念、想法,像是我們為什麼要做這個專案、這個專案和使用者的互動方式、你預期使用者會如何理解你的專案 ... 等等。
我們和 AI 之間就是缺乏共同的設計概念(Design Concept),才會導致 AI 做出來的東西和我們要的不一樣。
AI 用的詞彙和你用的詞彙沒有對齊,導致溝通成本變高。
我自己在使用 AI 時發現他很喜歡賣弄名詞,像我最近遇到的有 “薄 Wrapper”、“架構摩擦點”、“太低太飄”,雖然能理解他意思,但就是要花點時間思考。
這讓 Matt 想到軟體開發裡一個老問題:就是當開發者和領域專家(domain expert)之間沒有建立共同語言時,翻譯成本會增加,影響開發效率。
程式碼看起來沒錯,但就是不 work 或有奇怪的問題。
明顯的解法是建立回饋迴路(feedback loops),像是 TypeScript 的靜態型別檢查、讓 AI 能存取瀏覽器的工具、自動化測試 ... 等等能讓 AI 提早發現問題的方法。
不過即使有這些工具,AI 其實也不太會善用它們,像是可能會為了通過測試而把測試刪掉、或寫一堆不著邊際的測試、又或者寫出超複雜的 TS 型別等等。
《The Pragmatic Programmer》把類似現象稱為 “outrunning your headlights”。
意思是,夜間開車時,我們只能看見車燈照得到的範圍;如果開得太快,就會進入還看不清楚、也來不及反應的地方,因此風險會快速上升。
放到 AI 開發裡也是一樣。AI 能生成程式碼的速度很快,但專案真正能前進多快,取決於我們能理解、驗證與修正的速度。換句話說,瓶頸不一定在 AI,而是在人類是否跟得上它產出的內容。
如果我們還沒理解設計意圖、邊界條件、錯誤處理與測試結果,就讓 AI 繼續瘋狂開發,表面上看起來進展很快,實際上只是不斷累積不確定性和風險而已。
等到錯誤累積到一定程度,後面要回頭整理、除錯或重構,成本會反而會高到無從下手。
尤其 AI 的預設行為是先大量輸出程式碼,事後才想到要跑測試,以前這樣的問題可能不大,因為速度慢,發生問題還能補救,但 AI 速度太快了,這可能會導致在發現問題時,錯誤已經很嚴重了。
當 codebase 充滿大量細碎的小模組時,AI 在探索程式碼時容易迷路,找不到對的地方,也搞不清楚各模組之間的依賴關係。
Matt 把這種結構稱為「shallow modules」,也就是介面複雜但功能很少的小模組,這也是 AI 特別擅長產出的程式碼結構,AI 不會主動去思考怎麼寫出好維護的程式碼,他會先以完成功能為主。
現在 AI 時代,我們能交付的程式碼的確比以前多,但卻比以前更累。AI 的出現只是解放我們的雙手,但沒有解放我們的大腦。
因為我們不只要讓 AI 理解 Codebase,我們自己也要去理解必要的細節,甚至還要思考如何讓 AI 理解我們的需求,遵循我們的開發方式。
換句話說,以前只需要我們自己理解需求,自己思考如何實作就好,現在變成我們理解完,還要想辦法讓 AI 理解,並且確保他實作方向是對的。
而如果 Codebase 的結構很複雜,整體認知負擔、心智成本就會增加許多。
為了解決失敗模式一,Matt 做了一個叫 Grill Me 的 prompt skill,核心概念是:「對這個計畫的每一個面向,持續不斷的問我,直到我們對設計達成共同理解。沿著設計樹的每一條分支,逐一解決決策之間的依賴關係。」
這個 skill 可能會讓 AI 對你提出40、60 甚至 100 個問題,AI 會不斷追問直到雙方真正理解彼此。
對話的結果可以轉化成 PRD(產品需求文件),或者直接轉成 issues 讓後續的 agent 處理。
Matt 認為這比直接跳進 plan mode 更有價值,因為 plan mode 太急著產出執行計畫,而沒有先確認雙方是否真的對齊。
我在使用後,覺得真的能幫助我釐清一些思考與設計上的盲點,像我最近在做一個類似線上課程的平台,他就會問我空的課程能不能發佈?需不需要多一個 published 來控制?空內容發佈之後要顯示什麼 UI?等等問題
有些設計在初期常常想的不夠完整,利用 gill me skill 就能解決這個問題,並把結果寫成文檔儲存。
這個概念來自 DDD(Domain-Driven Design)。
通用語言的意思是:開發者之間的對話、程式碼裡的命名、跟領域專家的溝通,全部使用同一套從領域模型衍生出來的詞彙。
對 AI 來說,這意味著你需要一份 markdown 文件,把你們之間共同使用的術語整理清楚。
Matt 做了一個 skill 讓 AI 自動掃描 codebase 來找到共同術語,生成這份通用語言文件。
他發現這份文件不只改善了規劃品質,還讓 AI 在思考時更精準,最終產出的程式碼也更貼近原本的計畫。
針對失敗模式三和四,解法是 TDD(Test-Driven Development)。
TDD 的規則會強迫 AI 先寫測試,再讓測試通過,再重構——每一步都很小,回饋迴路一直在運作。
但測試本身很難,因為你必須決定:測試的單元要多大?要 mock 什麼?要測哪些行為?這些決定全部互相牽連。
而且,測試難不難寫,其實反映了你的 codebase 品不品質,好的 codebase 容易測試,不好的 codebase 很難測試,這就回到了程式碼本身的設計問題。
此外,測試的寫法應該是:
Test 1 -> Implement 1 -> Test 2 -> Implement 2 -> Test 3 -> Implement 3 ...
而不是 Test 1 -> Test 2 -> Test 3 -> Implement 1 -> Implement 2 -> Implement 3 ...
這樣小步前進,除了失敗原因更單純,更好 Debug 以外,也能讓我們慢慢把程式設計,像是介面、資料形狀、錯誤處理、命名等面向筆的更完善
一開始就把所有測試寫好,其實反而會限制我們的設計和思考。
Matt 引用 John Ousterhout 的《A Philosophy of Software Design》裡的概念。
一個充滿 Shallow Modules 的 Codebase 就是一堆散亂的 Function 和邏輯,除了我們很容易迷路以外, AI 一樣需要看一堆檔案,導致品質下降。(而 AI 自己卻很喜歡寫出這樣的 Codebase)
而一個有 Deep Modules 的 Codebase,模組之間有清楚的邊界,介面在上層,實作在裡面。
我們在介面層測試,用介面來驗證行為,這樣的 Codebase 才能用好 TDD。
Matt 做了一個「Improve Codebase Architecture」的 skill 來自動做這件事,他會掃描 Codebase,找到相關的程式碼,把它們包進一個 deep module 裡。
我試用之後,的確是解決了邏輯散亂的問題,更強迫我們把 Domain 切好,讓程式碼更穩健。
最後這個 tip 同時解決失敗模式五和六。
當你的 Codebase 有清楚的模組邊界和簡單的介面,我們可以採取一種叫做「Gray Box」的心態:意思是我們仔細設計模組的介面,但不需要深入追每一行實作細節,把那個部分交給 AI。
我們只需要記住介面和模組的目的,而不是所有的實作細節。這樣能大幅降低認知負擔,讓你能夠長期維持開發效率。
這也是 Deep Module 的好處,設計介面與架構,實作細節交給 AI。
我們可以把把 AI 想成一個很強的特種兵,它很會做戰術層的事情,但它需要一個在戰略層思考的人,成為那個人就是我們的價值。
而戰略層思考需要的,正是我們已經用了幾十年的軟體工程基本功,像是設計、模組化、測試、通用語言、系統思維等等。
Specs To Code 的根本問題,在於它很容易讓我們放棄對系統設計的持續投入,轉而只關心能不能用最快速度產出看得見的成果。
這讓我想到之前讀《重構(第二版):改善既有程式的設計》時提到的一個觀念:重構不應該被視為某個大型階段,也不是等到系統爛到不能維護時,才一次性投入大量時間處理。更健康的方式,是在日常開發中持續、小步地改善既有設計。
軟體設計也是如此。它不是在開發前寫完規格,然後交給 AI 一次生成程式碼就結束的事情。真正的設計,往往是在實作、測試、閱讀程式碼、理解邊界條件與修正錯誤的過程中逐步浮現。
如果我們把 Specs To Code 當成主要開發模式,就很容易跳過這些必要的理解與調整。
所以說 Code is not cheap. Code matters. And so do you.