AI 寫程式超快,但你跟得上嗎?Netflix 的三階段解法
Netflix 工程師 Jake Nations 分享 AI 開發的核心困境:程式碼生成速度已超過工程師理解速度。他提出 Research、Planning、Implementation 三階段方法論,強調「理解先於生成」。本文整理他對本質複雜性與意外複雜性的區分、Easy 與 Simple 的混淆陷阱,以及如何讓 AI 真正發揮效用而非製造更多技術債。
「我部署過我不完全理解的程式碼。我生成了它、測試了它、部署了它。但我無法解釋它是怎麼運作的。」
這是 Netflix 工程師 Jake Nations 在一場技術演講中的開場白。台下一片安靜,接著他補了一句:「我願意打賭,你們每一個人也都做過。」掌聲響起。
Jake 過去幾年在 Netflix 負責推動 AI 工具的採用。他親眼見證 AI 帶來的加速——過去需要好幾天才能完成的工作,現在幾小時就能搞定;擱置多年的大型程式碼重構,終於有人手去執行。但他也注意到一件令人不安的事:程式碼生成的速度,已經超過工程師理解的速度。
大型系統總是會在意想不到的地方掛掉。當那一刻來臨,你最好搞得懂自己正在除錯的程式碼。問題是,AI 一秒鐘能產出上千行程式碼,我們的理解力根本追不上。
軟體危機簡史:這不是第一次
Jake 說,這種「複雜性超出管理能力」的困境,其實不是什麼新鮮事。每一代軟體工程師都碰過這堵牆。
1960 年代末,一群聰明的電腦科學家聚在一起,發現了一個問題:社會對軟體的需求暴增,但軟體開發的速度根本跟不上。專案延遲、成本超支、品質低落。他們把這個現象稱為「軟體危機」。
荷蘭電腦科學家 Edsger Dijkstra 當時說過一句話:「當我們只有幾台弱小的電腦時,程式設計只是個小問題;現在我們有了巨大的電腦,程式設計也變成了巨大的問題。」他的觀察是:硬體能力每成長一千倍,社會對軟體的期待也會跟著漲一千倍,留給工程師的只有如何在「想要」和「做得到」之間找到平衡。
這種循環不斷重演。1970 年代有了 C 語言,可以寫更大的系統。1980 年代個人電腦普及,人人都能寫程式。1990 年代物件導向程式設計流行,帶來了複雜到讓人頭痛的繼承結構。2000 年代敏捷開發興起。2010 年代雲端、行動裝置、DevOps 全面爆發,軟體真的「吃掉了世界」。
而現在,我們有了 AI。Copilot、Cursor、Claude、Codex、Gemini——我們可以用描述的速度生成程式碼。模式依然在重複,但這次的規模是「無限」的。
Easy vs Simple:AI 時代最危險的混淆
「我們陷入了一個陷阱,」Jake 說,「我們混淆了『容易』和『簡單』。」
這兩個詞聽起來很像,但它們指向完全不同的東西。Clojure 程式語言的創造者 Rich Hickey 在 2011 年有一場著名的演講叫做「Simple Made Easy」,對這兩個概念做了精確的區分。
Simple(簡單) 的字源來自拉丁文,意思是「一個折疊、一個編織」,也就是沒有糾纏。一個簡單的系統,每個部件各司其職,彼此不會糾纏在一起。你改動一個地方,不會牽動其他十個地方。
Easy(容易) 的字源則是「鄰近」。什麼東西離你近、伸手就能拿到,那就是容易的。安裝一個套件、從 Stack Overflow 複製一段程式碼、讓 AI 生成一個功能——這些都是「容易」的事。
重點來了:你沒辦法靠許願讓事情變簡單。簡單需要思考、需要設計、需要花力氣把糾纏的東西理清楚。但你隨時都可以讓事情變得更容易——只要把它放得更近就好。
人類天生會走阻力最小的路,這是生存本能,沒什麼好怪的。在 AI 出現之前,這種取捨還算可控——複雜性在程式碼庫中累積的速度夠慢,工程師有時間重構、重新思考、必要時砍掉重練。
但 Jake 認為 AI 打破了這個平衡。AI 是終極的「容易製造機」,讓容易的路變得毫無阻力,以至於我們根本不再考慮簡單的那條路。程式碼瞬間就能出現,誰還會停下來想架構?
他用一個例子說明這種惡化是怎麼發生的:假設你要在應用程式裡加一個登入功能。你跟 AI 說「加個身份驗證」,它生成了一個檔案。你再說「也支援第三方登入」,又多了一個檔案。你持續迭代,到了第二十輪對話,你發現 session 壞了、出現一堆衝突。這時候你已經不是在討論功能了,你是在管理一個連你自己都記不清所有限制條件的混亂脈絡。裡面有廢棄方案留下的死代碼、為了讓測試通過而硬湊的修補、三種不同解法的殘骸。
每一次互動,都是選擇「容易」而不是「簡單」。容易意味著你可以快速把東西加進系統;簡單意味著你能理解你做了什麼。當我們每次都選容易,複雜性就會不斷堆疊,直到某天爆發。
本質複雜性 vs 意外複雜性:AI 看不見的接縫
軟體工程界另一位重量級人物叫 Fred Brooks,他寫過經典著作《人月神話》。1986 年,他發表了一篇論文〈沒有銀彈〉,主張不會有任何單一創新能讓軟體生產力提升一個數量級。
為什麼?因為他認為軟體開發的困難從來就不在「打字」。語法、樣板程式碼、重複的機械性工作——這些都是表面。真正困難的是理解問題本身,然後設計出解決方案。沒有工具可以幫你繞過這個根本難題。
Brooks 把軟體的複雜性分成兩種。本質複雜性(Essential Complexity)是問題本身的難度——使用者需要付款、訂單必須被處理、資料要符合法規。這是你的軟體存在的理由,砍不掉。
意外複雜性(Accidental Complexity)則是我們自己加進去的東西——權宜之計、過時的防禦性程式碼、當初有道理但現在已經沒人記得為什麼的抽象層。這些是「讓程式碼能跑起來」的副產品,不是業務本身需要的。
在真實的程式碼庫裡,這兩種複雜性糾纏在一起。要分辨哪些是必要的、哪些只是歷史遺留的包袱,需要脈絡、需要經驗、需要有人記得「當初為什麼這樣寫」。
問題是,AI 看不到這條接縫。你讓 AI 分析一個程式碼庫,它會把每一行都當成「該保留的模式」。第 47 行的權限檢查是模式,2019 年某人寫的那段奇怪程式碼(明明是 gRPC 卻寫得像 GraphQL)也是模式。技術債不會被認定為債,它只是「更多程式碼」。
Netflix 的真實案例:授權系統重構的困境
Jake 分享了一個他在 Netflix 親身經歷的案例。他們有一個系統,在舊的授權邏輯和新的集中式授權系統之間加了一層抽象層——當初沒時間全面改寫,就先弄了一個墊片撐著。
現在有了 AI,看起來是個好機會:用 AI 來重構,把舊程式碼直接遷移到新系統。聽起來很合理吧?
結果完全行不通。舊程式碼和授權模式耦合得太緊密——權限檢查散落在業務邏輯裡、角色假設被寫死在資料模型中、授權相關的呼叫散布在數百個檔案裡。AI 開始重構,處理了幾個檔案就撞上一個解不開的依賴,接著整個失控。
更慘的是,AI 有時候會「好心」地把舊系統的邏輯用新系統重新實作一遍——完全搞錯方向。它看不到接縫,分不清業務邏輯在哪裡結束、授權邏輯又從哪裡開始。當意外複雜性糾纏到這種程度,AI 幫不了忙,只會在上面再疊更多層。
三階段解法(一):Research——把大象裝進盒子裡
Jake 負責的那個 Netflix 程式碼庫,主服務大約有五百萬個 token。沒有任何 AI 的脈絡視窗(context window)裝得下它。
一開始他想偷懶:把大段大段的程式碼丟進去,看 AI 能不能自己找出模式、搞清楚發生什麼事。結果就跟之前的授權重構一樣,AI 在自己的複雜性裡迷路了。
這逼得他換一種做法:不能讓 AI 自己去挖掘,得先告訴 AI「什麼才是重要的」。
想像你要請一個剛入職的新人幫忙處理一個專案。你不會把公司十年來的所有文件全部丟給他,然後說「你自己看著辦」。你會先跟他解釋背景:這個專案的目標是什麼、有哪些關鍵的元件、過去踩過哪些坑、這次改動會影響到哪些地方。
Jake 開始做的就是這件事。他把架構圖、設計文件、關鍵介面的定義、甚至相關的 Slack 討論,都整理起來餵給 AI,然後請 AI 分析程式碼庫,畫出元件之間的依賴關係。
但這不是一次性的作業。Jake 會不斷追問:「那快取的部分呢?」「失敗的時候怎麼處理?」AI 分析有錯,他就糾正;AI 缺少脈絡,他就補充。每一輪迭代都在精煉 AI 的理解。
這個階段的產出,是一份研究文件。五百萬個 token,被壓縮成大約兩千字的規格說明。這份文件記錄了:現有的系統長什麼樣、什麼跟什麼連在一起、這次的改動會影響哪些地方。
Jake 特別強調這裡有一個「人工檢查點」,這是整個流程中槓桿效應最高的時刻——你在這裡抓到錯誤,就能預防後面的災難。AI 的分析終究需要人來驗證,因為只有人知道那些沒寫在程式碼裡的潛規則。
用一個生活化的比喻:這個階段就像蓋房子之前的地質調查和測量。你不會拿到一塊地就直接開始動工。你要先搞清楚這塊地的邊界在哪裡、地底下有沒有管線、土壤能不能承受你想蓋的結構。這些前置作業看起來不像在「蓋房子」,但沒有它們,房子遲早會出問題。
三階段解法(二):Planning——把藍圖畫到連學徒都能照著做
有了 Research 階段產出的研究文件,接下來是 Planning。
這個階段的目標,是產出一份詳細到「照著做就會動」的實作計畫。Jake 把它比喻成「按數字塗色」(paint by numbers)——就是那種每個區塊都標好數字、對應特定顏色、只要照著填就能完成一幅畫的繪圖遊戲。
具體要包含什麼?程式碼的結構、函式的簽名、型別的定義、資料怎麼流動。詳細到什麼程度?Jake 說,你應該能把這份計畫交給團隊裡最菜的工程師,跟他說「照這個做」,他逐行照做,東西就應該要能動。
為什麼要這麼詳細?因為這個階段就是做架構決策的地方。
複雜的邏輯在這裡被攤開來檢視:這樣寫對嗎?有沒有漏掉什麼邊界情況?業務需求有沒有被正確反映?服務跟服務之間的邊界夠清楚嗎?有沒有不必要的耦合被引進來?
這些問題必須在程式碼被寫出來之前解決。因為一旦程式碼生成出來,人就會有種「已經做完了」的錯覺,變得不想回頭檢視。Jake 觀察到一件事:AI 不會對糟糕的架構決策提出抗議。你跟它說「讓這個功能動起來」,它就會讓它動起來;你說「修掉這個錯誤」,它就會修掉。程式碼會乖乖變形去滿足你最新的要求,但沒有人會攔住你說「等等,這樣的設計不太對」。
回到蓋房子的比喻:這個階段就是畫建築藍圖。你把地質調查的結果(Research)拿來,開始規劃房子的格局:幾層樓、幾個房間、樓梯在哪裡、水電管線怎麼走。藍圖要詳細到施工團隊可以直接照著蓋,不用一直跑來問「這裡你想怎麼做」。
藍圖階段的另一個好處是:審查速度變快了。比起審查一大坨程式碼,審查一份結構化的計畫文件要輕鬆得多。你可以在幾分鐘內就確認「這是不是我們要蓋的東西」,而不用花幾小時去理解程式碼。
三階段解法(三):Implementation——讓 AI 去做 AI 擅長的事
有了經過驗證的研究文件,加上一份詳細的實作計畫,Implementation 階段就變得相對單純。這就是設計它的初衷:思考已經完成,AI 才能真正發揮。
這個階段可以放手讓 AI 去寫程式碼。它有明確的規格可以遵循,脈絡保持乾淨而聚焦,不會陷入前面說的那種「五十則對話後的混亂」。
Jake 說,這時候甚至可以用「背景代理」(background agent)來處理——讓 AI 在背景自己跑,你去做別的事,回來再審查結果。這之所以可行,是因為你不是在試圖理解「AI 發明了什麼」,而是在確認「它有沒有照著計畫做」。
審查的心態完全不同。在沒有前兩階段的情況下,你審查 AI 生成的程式碼,心裡要不斷問:「這是什麼?」「為什麼這樣寫?」「這會不會有副作用?」每一行都是未知的。但有了計畫之後,你只需要問一個問題:「這有沒有符合計畫?」
這就像房子蓋好之後的驗收。你拿著藍圖逐項對照:牆在這個位置嗎?對。門開的方向對嗎?對。插座的位置呢?對。你不需要重新思考「房子應該長什麼樣」,因為那是藍圖階段已經決定好的事。你只需要確認施工有沒有照著藍圖走。
Jake 強調,這三個階段不是什麼魔法。它之所以有效,是因為第一階段(Research)迫使你去理解系統,第二階段(Planning)迫使你做出架構決策,第三階段(Implementation)才讓 AI 去做它真正擅長的事:快速生成符合規格的程式碼。
回到那個授權系統重構的案例。最後讓它成功的,不是找到更好的 prompt,也不是等模型變得更強,而是 Jake 的團隊先手動做了一次遷移——沒有 AI,就是人去讀程式碼、理解依賴、改動之後看哪裡壞掉。這個過程很痛苦,但它揭露了所有隱藏的限制條件:哪些假設不能打破、哪些服務會連帶受影響。
然後,他們把這個手動遷移的 pull request 餵給 AI,當作 Research 階段的素材。AI 有了「乾淨遷移長什麼樣」的範本,後續的遷移才開始順利起來。
理解先於生成。你得先親自走過一遍,才能教 AI 怎麼走。
結語:問題不是要不要用 AI
「我們還會用 AI 嗎?這已經是定論,船已經啟航了。」Jake 在演講尾聲這樣說。
真正的問題是:當 AI 寫了我們大部分的程式碼,我們還能理解自己的系統嗎?
每一代工程師都面臨過自己的軟體危機。Dijkstra 那一代用建立「軟體工程」這門學科來應對;我們這一代,面對的是無限的程式碼生成能力。AI 改變了我們寫程式的方式,但它沒有改變軟體失敗的原因。
Fred Brooks 四十年前說的話,今天依然成立:困難的從來不是打字,而是知道該打什麼。
能茁壯成長的開發者,不會是那些生成最多程式碼的人,而是那些理解自己在做什麼的人、能看見系統接縫的人、能認出「我們正在解決錯誤問題」的人。
這些判斷,目前只有人能做。
至少現在還是。