I Shipped Code I Don’t Understand and I Bet You Have Too — 我交付了自己不理解的程式碼,我打賭你也是

Netflix Staff 工程師 Jake Nations 在演講中坦承自己曾交付不完全理解的 AI 生成程式碼,並指出這不是個人問題,而是整個產業面臨的新一輪軟體危機。他從軟體工程歷史出發,剖析「簡單」與「容易」的根本差異,提出三階段方法(研究、規劃、實作)來對抗 AI 帶來的無限複雜度,強調在 AI 時代,思考與理解才是工程師最不可替代的能力。


原影片連結:https://www.youtube.com/watch?v=eIoohUmYpGI

影片重點

  • AI 程式碼生成的加速是真實的,但理解速度跟不上生成速度,形成知識鴻溝
  • 軟體複雜度危機不是新問題,每一代工程師都曾面對,只是 AI 讓規模變成無限
  • Fred Brooks 在 1986 年就指出:沒有銀彈,真正困難的是理解問題本身而非打字
  • Rich Hickey 區分的「Simple vs Easy」是理解 AI 陷阱的關鍵框架
  • AI 是終極的「Easy Button」,讓人不再思考架構就直接產出程式碼
  • AI 無法區分「本質複雜度」與「意外複雜度」,會把技術債當成模式保留
  • 三階段方法:Research → Plan → Implement,讓思考成為主要工作
  • 有時必須先手動完成一次遷移,才能建立足夠理解讓 AI 接手
  • 軟體是人類的事業,真正的難點從來不是寫程式碼,而是知道該寫什麼

詳細內容

[00:20] 開場坦白:我們都交付了不理解的程式碼

Jake Nations 以一段坦白開場:他曾用 AI 生成程式碼,測試通過、部署上線,但自己無法解釋它是怎麼運作的。他打賭在場每個人都做過一樣的事。這段坦白引起了全場掌聲。

他在 Netflix 負責推動 AI 工具的採用,親眼見證了加速效果:過去需要數天的 backlog 項目現在只要幾小時,擱置多年的大型重構終於得以完成。但他也看到了問題的另一面——大型生產系統總是以意想不到的方式失敗,當它們失敗時,你最好能理解你正在除錯的程式碼。

[01:48] 歷史的循環:軟體危機不是新鮮事

Jake 回顧了軟體工程的歷史,指出每一代工程師都曾面對軟體複雜度超越管理能力的時刻。1960 年代末到 1970 年代初,一群電腦科學家聚在一起宣告「我們正處於軟體危機」。Dijkstra 說過一句話:當我們只有幾台弱小的電腦時,程式設計只是個小問題;現在我們有了巨型電腦,程式設計也變成了巨大的問題。

這個循環不斷重複:70 年代的 C 語言讓我們能寫更大的系統;80 年代個人電腦讓每個人都能寫程式;90 年代物件導向帶來了「繼承地獄」;2000 年代敏捷開發和 Scrum;2010 年代雲端、行動、DevOps——軟體真的吃掉了世界。而今天,我們有了 AI:Copilot、Cursor、Claude、Codex、Gemini,我們能以描述的速度生成程式碼。模式延續了,但規模真正變成了無限。

[03:25] Fred Brooks 的「No Silver Bullet」

Jake 引用了 Fred Brooks 1986 年的經典論文《No Silver Bullet》。Brooks 論證不會有任何單一創新能讓軟體生產力提升一個數量級。原因在於——真正困難的部分從來不是編碼的機械動作(語法、打字、樣板程式碼),而是理解實際問題並設計解決方案。沒有任何工具能消除這個根本難度。

迄今為止,我們創造的每一個工具和技術都讓機械動作變得更容易,但核心挑戰——理解該建造什麼、它應該如何運作——仍然一樣困難。

[04:22] Simple vs Easy:我們混淆的兩個詞

如果問題不在機械動作上,為什麼我們一直在為它最佳化?Jake 引用了 Clojure 程式語言創造者 Rich Hickey 在 2011 年的經典演講《Simple Made Easy》。

Rich Hickey 定義了兩個關鍵概念:

  • Simple(簡單):意味著「一折、一縷、不糾纏」。每個部件只做一件事,不與其他部件交織。簡單關乎的是結構
  • Easy(容易):意味著「鄰近的」。觸手可及的東西,不費力就能使用。複製、貼上、交付。容易關乎的是接近性

我們無法靠許願讓事物變簡單——簡單需要思考、設計和解糾纏。但我們總是能讓事物變得更容易——只要把它放得更近:安裝一個套件、用 AI 生成、從 Stack Overflow 複製。

每次我們選擇「容易」,我們選擇的是現在的速度、之後的複雜度。以前這個取捨是可控的,因為複雜度累積得夠慢,我們有時間重構。但 AI 摧毀了這個平衡——它是終極的「Easy Button」,讓容易的路徑如此無摩擦,我們甚至不再考慮簡單的那條路。

[06:10] 示範:一個簡單任務如何演變成複雜的混亂

Jake 用一個虛構的例子示範了這個過程。從一個簡單的應用開始,你要求 AI 加上認證功能,得到一個乾淨的 auth.js 檔案。接著迭代幾次、加上 OAuth,然後發現 session 壞了、出現衝突。到了第 20 輪對話,你已經不是在討論了,而是在管理一個複雜到連自己都不記得所有限制條件的 context。

遺留下來的是:被放棄的方法留下的死程式碼、靠著「讓測試通過就好」修復的測試、三個不同解決方案的碎片。每一條新指令都在覆寫架構模式。AI 對糟糕的架構決策毫無抵抗——程式碼只是不斷變形來滿足你最新的要求。

[07:41] 本質複雜度 vs 意外複雜度

回到 Fred Brooks 的論文,Jake 解釋了兩種複雜度:

  • 本質複雜度(Essential Complexity):你試圖解決的實際問題的根本難度。用戶需要付款、訂單必須被履行——這是你的軟體系統存在的原因。
  • 意外複雜度(Accidental Complexity):我們一路上添加的其他一切。繞路方案、防禦性程式碼、曾經有道理的框架和抽象層。

在真實的程式碼庫中,這兩種複雜度無處不在且糾纏在一起,分離它們需要 context、歷史和經驗。但 AI 生成的程式碼不做這樣的區分——每一個模式都被同樣保留。技術債不會被識別為債務,它只是更多的程式碼。

[08:30] Netflix 的真實案例:授權重構

Jake 分享了一個 Netflix 的真實案例。他們有一個抽象層介於舊的授權程式碼和新的集中式授權系統之間——一個五年前寫的 shim。現在有了 AI,理應是重構的好時機。

結果呢?舊程式碼與授權模式緊密耦合:權限檢查穿插在業務邏輯中、角色假設烘焙在資料模型裡、授權呼叫散布在數百個檔案中。AI 代理開始重構,處理了幾個檔案後就碰到無法解開的依賴關係,要不是失控放棄,就是更糟——試圖用新系統重建舊系統的邏輯。

AI 看不到接縫。它無法辨識業務邏輯在哪裡結束、授權邏輯在哪裡開始。當意外複雜度糾纏到這種程度,AI 不是最好的幫手,它只會在上面再加更多層。

[10:27] Context Compression:三階段方法

Jake 介紹了他的方法,他稱之為「Context Compression」(也被稱為 Context Engineering 或 Spec-Driven Development)。核心理念是:思考和規劃成為工作的主體

Netflix 的主要程式碼庫有約一百萬行 Java,主服務大約五百萬 tokens——沒有任何 context window 能裝下。他一開始試著把大量程式碼塞進 context,但輸出同樣迷失在自身的複雜度中。於是他被迫做不一樣的事:精心選擇要納入的內容——設計文件、架構圖、關鍵介面,並花時間寫出元件應如何互動的需求。五百萬 tokens 被壓縮成 2,000 字的規格說明。

[11:10] 第一階段:Research(研究)

把所有相關資訊前置提供給 AI:架構圖、文件、Slack 討論串。然後用代理分析程式碼庫,映射出元件和依賴關係。

這不應該是一次性的過程。Jake 會持續追問:快取怎麼處理?故障時怎麼辦?當 AI 的分析有誤,他會糾正;當缺少 context,他會補充。每次迭代都在精煉分析。

輸出是一份單一的研究文件:這裡有什麼、什麼連接到什麼、你的變更會影響什麼。數小時的探索被壓縮成幾分鐘的閱讀。這裡的人工檢查點至關重要——這是你在這裡抓住錯誤、預防後續災難的最高槓桿時刻。

[12:19] 第二階段:Plan(規劃)

基於驗證過的研究,創建詳細的實作計畫:真正的程式碼結構、函式簽名、型別定義、資料流向。這個計畫要詳細到任何開發者都能照做——就像按數字填色一樣,交給最資淺的工程師,逐行照做就能運作。

這一步是做出重要架構決策的地方:確保複雜邏輯正確、業務需求遵循好的實踐、服務邊界清晰、防止不必要的耦合。我們能在問題發生前發現它們,因為我們經歷過它們。AI 沒有這個選項——它把每個模式都當成需求。

真正神奇的是審查速度:我們能在幾分鐘內驗證計畫,確切知道將要建造什麼。

[13:17] 第三階段:Implement(實作)

有了清晰的計畫和經過驗證的研究,實作階段應該相當簡單——這正是重點。當 AI 有清晰的規格可遵循,context 保持乾淨和聚焦。我們防止了長對話的複雜度螺旋。

與其 50 條訊息的演化式程式碼,我們有三個聚焦的輸出,每個在進入下一步前都經過驗證。沒有被放棄的方法、沒有衝突的模式、沒有留下死程式碼的「等等,其實…」時刻。

真正的回報是可以用背景代理執行實作——因為你已經提前完成了所有思考和困難的工作。它可以開始實作,你去做別的事,回來審查時只需要驗證它是否符合你的計畫,而不是試圖理解它是否發明了什麼新東西。

[14:45] 回到授權重構:手動遷移的必要性

回到那個 AI 處理不了的授權重構。Jake 的團隊發現他們甚至不能直接跳入研究、規劃、實作的流程。他們必須先親手完成一次遷移——沒有 AI,只有閱讀程式碼、理解依賴關係、做出改動看什麼會壞掉。

這次手動遷移很痛苦,但至關重要。它揭示了所有隱藏的限制條件:哪些不變量必須保持為真、哪些服務會在授權改變時崩潰——這些是再多的程式碼分析都無法浮現的東西。

然後他們把這個手動遷移的 Pull Request 餵入研究流程,作為後續研究的種子。AI 終於能看到一個乾淨的遷移長什麼樣。即便如此,每個實體都略有不同,仍然需要不斷追問和提供額外 context。三階段方法不是魔法——它之所以能運作,是因為他們先手動完成了一次遷移,在能把理解編碼進流程之前,必須先贏得那份理解。

[16:02] 不存在銀彈

Jake 認為不存在銀彈——不是更好的 prompt、更好的模型,甚至不是寫更好的 spec。真正必要的工作是深入理解你的系統,深到你能安全地對它做出改變。

「它能用」是不夠的。通過測試的程式碼和能在生產環境存活的程式碼之間有差距。今天能運作的系統和未來別人能修改的系統之間有差距。

[16:37] 知識鴻溝與能力萎縮

當 AI 能在幾秒內生成數千行程式碼,理解它可能需要數小時,甚至數天。更令人擔憂的是——每次我們跳過思考來跟上生成速度,我們不只是在添加不理解的程式碼,我們正在失去辨識問題的能力。

那種「嘿,這正在變得複雜」的直覺,在你不理解自己的系統時會萎縮。模式辨識來自經驗——Jake 能識別危險的架構,是因為他是那個凌晨三點起來處理問題的人;他推動更簡單的解決方案,是因為他維護過別人留下的替代方案。AI 生成你要求的東西,但它不編碼過去失敗的教訓。

[17:27] 結語:軟體是人類的事業

AI 改變了我們寫程式碼的一切方式,但它沒有改變軟體本身為什麼會失敗的任何原因。每一代人都面對過自己的軟體危機:Dijkstra 那一代用創造軟體工程這門學科來應對,而我們面對的是無限程式碼生成的危機。

Jake 不認為解決方案是另一個工具或方法論。而是記住我們一直都知道的事:軟體是人類的事業。困難的部分從來不是打字寫程式碼,而是知道該寫什麼。

能蓬勃發展的開發者不只是生成最多程式碼的人,而是那些理解自己在建造什麼的人——那些仍然能看到接縫、能辨認出自己在解決錯誤問題的人。那仍然是我們,也只能是我們。

他最後留下一個問題:問題不是我們是否會使用 AI——那已成定局。問題是:當 AI 在撰寫我們大部分程式碼時,我們是否仍然能理解自己的系統?

我的想法

這場演講最有價值的觀點不是「AI 不好」或「不要用 AI」,而是精準地指出了問題所在:我們混淆了「容易」和「簡單」。Rich Hickey 的 Simple vs Easy 框架在 2011 年用來批評框架膨脹,放到 AI 時代竟然更加貼切。AI 把「容易」推到了邏輯極限——生成程式碼的摩擦趨近於零,但程式碼的複雜度不會自動趨近於零。

三階段方法(Research → Plan → Implement)本質上就是在 AI 工作流中重新引入「思考」這個步驟。這和 Context Engineering 的理念完全一致——與其讓 AI 在龐大的程式碼庫中盲目摸索,不如先由人類壓縮 context、定義邊界,再讓 AI 在清晰的框架內執行。

Netflix 授權重構的案例特別有啟發性:即使有了方法論,有些工作必須先手動完成一次。這提醒我們,AI 輔助開發不是從零開始的魔法,而是建立在人類理解之上的加速器。你必須先「贏得」那份理解,AI 才能有效地幫助你擴展它。

對於日常開發者的實際建議是:不要在 AI 對話中迭代 20 輪來解決問題。停下來,花時間寫一份規格說明,然後讓 AI 按照規格執行。前期多花的思考時間,會在後期省下十倍的除錯時間。

進階測驗:AI 時代的軟體複雜度與工程思維

測驗目標:驗證你是否能在實際情境中應用 Jake Nations 演講中的觀點與方法論。
共 5 題,包含情境題與錯誤診斷題。

1. 你正在用 AI 助手重構一個有三年歷史的支付模組 情境題

你的團隊打算用 AI 代理重構一個舊的支付模組。這個模組有大量的防禦性程式碼、 過時的錯誤處理方式、以及與已廢棄的第三方 API 的相容層。 你把整個模組(約 8000 行)丟進 AI 的 context,請它「重構為使用新的支付 API」。 AI 生成了新的程式碼,但保留了舊 API 的相容邏輯,並在上面又疊了一層新的抽象。
  • A. 增加更多 prompt 細節,明確告訴 AI 刪除舊的相容層
  • B. 換一個 context window 更大的模型,把更多周邊程式碼一起塞進去
  • C. 先寫一份規格文件,區分哪些是業務邏輯(本質複雜度)、哪些是舊實作的包袱(意外複雜度),再讓 AI 按規格執行
  • D. 讓 AI 在多輪對話中逐步修正,每次處理一小部分

2. 你的團隊想用 AI 遷移授權系統,但遇到困難 情境題

你的應用有一個五年前寫的授權抽象層(shim),介於舊的自建授權系統和新的集中式 授權服務之間。權限檢查散布在數百個檔案的業務邏輯中,角色假設烘焙在資料模型裡。 你嘗試用 AI 代理做遷移,但它每次處理幾個檔案後就碰到無法解開的依賴關係而失敗。 根據 Jake Nations 在 Netflix 的經驗,下一步最好的做法是什麼?
  • A. 提供更詳細的架構圖給 AI,讓它理解完整的系統依賴關係
  • B. 先手動完成一個實體的遷移(不用 AI),記錄所有隱藏的限制條件,再把這次遷移作為 AI 後續工作的參考範本
  • C. 把程式碼庫拆分成更小的模組,每次只讓 AI 處理一個獨立模組
  • D. 等更強大的 AI 模型發布後再嘗試,目前的模型 context window 不夠大

3. 你面對一個 500 萬 token 的 Java 程式碼庫需要做大型變更 情境題

你的 Java 微服務程式碼庫有約 500 萬 tokens,需要將日誌系統從 Log4j 遷移到 新的 OpenTelemetry 標準。沒有任何 context window 能裝下整個程式碼庫。 你嘗試把大量程式碼塞進 AI 的 context,但輸出品質很差。 根據三階段方法,你應該如何開始?
  • A. 研究階段:提供架構圖和關鍵介面文件,用 AI 分析並映射出元件依賴關係,反覆迭代修正分析結果,最終壓縮成一份精簡的研究文件
  • B. 直接開始撰寫詳細的實作計畫,列出所有需要修改的檔案和函式簽名
  • C. 把程式碼庫分成多個批次,每次塞入 context window 上限的程式碼量讓 AI 處理
  • D. 用 AI 代理自動掃描整個程式碼庫,讓它自行決定遷移策略

4. 開發團隊的 AI 輔助開發流程出了什麼問題? 錯誤診斷

小華的團隊使用以下 AI 輔助開發流程: 1. 在 AI 對話中描述需求:「幫我加上使用者認證」 2. 看到生成的 auth.js,覺得不錯,繼續要求:「加上 OAuth 支援」 3. 發現 session 衝突,要求:「修復 session 問題」 4. 測試發現新 bug,要求:「修正這個錯誤」 …(持續到第 30 輪對話) 最終程式碼能跑、測試也通過,但團隊中沒人能完整解釋認證流程的運作方式。 三個月後出現安全漏洞,整個團隊花了兩週才找到根本原因。 這個流程的根本問題是什麼?
  • A. AI 模型的能力不足,應該換用更強大的模型
  • B. 對話輪數太多,應該限制在 10 輪以內
  • C. 團隊選擇了「容易」而非「簡單」的路徑:每次互動都在用最小摩擦的方式解決眼前問題,卻不斷累積複雜度,最終失去對系統的理解能力
  • D. 缺少充分的自動化測試,應該增加測試覆蓋率

5. AI 重構的結果為什麼不理想? 錯誤診斷

小明讓 AI 代理重構一個舊的訂單處理系統。AI 分析了程式碼庫後產出以下結果: – 保留了一個三年前為了相容舊版 API 而寫的資料轉換層(該舊 API 已廢棄) – 保留了為了繞過前任工程師 bug 而加的 workaround 程式碼 – 把一段硬編碼的重試邏輯(retry 3 times, sleep 5s)複製到新的架構中 – 所有原本散落各處的 try-catch 區塊都原封不動地搬到新結構中 技術上程式碼可以運作,但跟原本一樣臃腫。 AI 犯了什麼根本性的錯誤?
  • A. AI 的 context window 不夠大,無法理解完整的程式碼結構
  • B. AI 無法區分本質複雜度和意外複雜度,將所有既有模式(包括技術債)都當成必須保留的需求來處理
  • C. AI 的訓練資料中缺少類似的重構案例,導致它不知道如何簡化
  • D. prompt 描述不夠明確,應該逐一列出哪些程式碼要刪除
0

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *