iT邦幫忙

2026 iThome 鐵人賽

DAY 13
0
Software Development

AI時代下的軟體工程系列 第 13 篇

Day13: 階段一回顧,與階段二的起點

  • 分享至 

  • xImage
  •  

Day5 到 Day12,我們完成了系列的第一個階段。在進入下一個階段之前,先回頭看看這個階段想解決什麼、做到了哪裡,又把哪些問題留給了後面。

階段一想解決什麼:Control

AI 再強,也不能保證每次交出的程式都是對的。它可能誤解需求、漏改呼叫端,或者寫出能跑、卻不符合預期的行為。因此,當 AI 對 codebase 做了一次修改,我們首先要回答的是:如何控制這次修改?

回頭看這八天的工具,其實大多不是在判斷程式「對不對」。Git 讓我們看見改了什麼,並在出問題時回復;formatter 讓 diff 只剩下邏輯上的改動;docs 則補上程式碼沒有交代的知識。它們與 linter、type checker、test 一起,組成了一個控制修改的迴圈:

AI 修改 → 看見修改(Git)→ 檢查 constraints(format / lint / type / test)
       → 補充知識(docs)→ 自動執行(hook / CI)→ 修正或回復 ↺

這個迴圈能成立,前提是檢查的標準不再只存在於人的腦中。在 prompt 裡提醒 agent「注意型別」、「記得補測試」,得依賴它每次都記得並正確套用;Day8 將它稱為提醒,而工具給出的,是驗證。

因此,階段一中人的角色,並不是逐行檢查程式,而是:把原本存在腦中的 constraint 外部化,讓人與 agent 都能自行執行、自行取得回饋。

階段一的 scope

這些 constraint 仍然需要由人定義。formatter 的風格、lint 的規則集、測試的預期輸出、docs 要記錄的內容,都需要有人決定;工具負責執行,判斷標準仍然來自人。

你可能也會好奇,為什麼階段一沒有談 AGENTS.md。AGENTS.md 適合放的,是「agent 無法從 repo 自己可靠推導、但會影響它怎麼工作」的規則,例如該執行哪些檢查指令。但這類規則並不只來自階段一,後面兩個階段也都會產生需要交代給 agent 的資訊。因此,它會留到三個階段都談完之後,在整合篇一起討論。

此外,階段一處理的單位是一次修改。它能確認這次修改是否符合 constraints,卻不回答另一個問題:要完成這次修改,我們需要理解多少程式?在談這個問題之前,先看一個 Day4 預告過、卻沒有在階段一獨立成篇的主題。

正在淡出的:局部可讀性

Day4 提到,AI 時代下,部分軟體工程的概念有機會淡出。其中最明顯的,是以 Clean Code 為代表的局部可讀性:命名要表達意圖、function 要短、參數不宜過多、巢狀不宜過深、避免 magic number、適時補上註解。

這些規則曾是軟體工程教育的核心,因為過去程式由人撰寫、由人閱讀,修改的成本也很高。人的工作記憶有限,只能從文字反推作者的意圖;一段難讀的程式,每位接手者都得付出一次理解成本。所以這些規則只能靠個人紀律與 code review 反覆要求。

coding agent 普及之後,這個前提改變了,最明顯的就是命名與註解。過去的 codebase 裡,不難看到 aaa、bbb 這類沒有意義的命名,甚至是拼錯的變數名稱,也有不少專案幾乎找不到一行 comment。但 coding agent 幾乎不會拼錯字,也不會寫出沒有意義的名稱,comment 更是預設就會補上;即使其他地方寫得不理想,局部重構的成本也幾乎可以忽略。

至於 function 長度、參數數量這類規則,大多有固定的判斷方式,本身就能寫成 lint 規則,例如 Day8 提到的 PLR0913(參數過多)與 C901(分支複雜度過高);它們在階段一,就已經從個人紀律轉成了專案規則。

換句話說,局部可讀性不再需要靠個人紀律維持,而是成為工具與 agent 共同守住的底線。

但局部可讀性解決的,只是「讀懂一段程式」的成本。真正困難的,往往是另一件事:要改好這一段,還得讀懂多少其他的程式?

沒有淡出的:複雜度

Day3 談過,context 有其限制:資訊過量時,每個東西都是重點,就變成沒有重點;無關的雜訊,也會拉低輸出的品質。這個限制不只屬於 AI。任何有一定規模的系統,都大到沒有人能一次理解全部。

理想的狀況,不是把整個系統塞進 context,讓 agent 什麼都知道;而是當我們要修改付款功能時,只需要理解付款的部分,以及它與其他部分之間的約定,不必知道通知、庫存、會員是怎麼實作的。

問題是,這件事並不會自然成立。getUser() 除了讀取資料,還會寫入一筆紀錄,呼叫它的人就不能只看名稱,必須讀進實作;同一條折扣規則散落在三個模組裡,修改時就得找出每一處;一個 class 同時負責計價、庫存與通知,想改計價,就得先理解另外兩件事。

這些程式都能通過階段一的所有檢查,即使測試完整涵蓋,也會全部通過,因為程式的行為是正確的。問題在於,完成一次修改所需要知道的資訊量,已經超出了應有的範圍。

對 coding agent 而言,這個代價更加直接。agent 往往只讀進部分檔案,一旦需要知道的範圍超出它讀到的內容,就容易漏改;Day9 的 type checker 能抓到其中一部分,卻無法抓到所有隱藏的依賴。而即使 context 從 200K 擴大到 2M,這個原理也不會改變:能塞進去,不代表每次修改都應該依賴整個系統。

階段二:Local Reasoning

這就是階段二要處理的問題:如何只理解系統的一小部分,仍然能安全地修改它?

答案是建立 boundary。structure 的價值並不在於整齊,而在於降低完成一次修改所需要知道的資訊量。function、class、module、interface,以及把商業邏輯與 DB、網路等副作用切開的邊界,都是達成這件事的手段:function 讓我們只看名稱與輸入輸出,就能暫時不必理解實作;class 決定 state 與 invariant 由誰負責;module 讓實作細節不外溢;interface 則讓兩邊不必互相知道對方的實作。

具備辨識與建立 boundary 的能力之後,有兩件事會同時變得可能:讀懂 AI 產出的結構,以及向 AI 說明我們預期的分工。這兩件事看似方向相反,底下其實是同一個能力。

至於「這個 boundary 值不值得建立」,則不是階段二能回答的問題。boundary 本身也有成本,而 agent 很容易為了不存在的需求,先寫出一層又一層的抽象。如何為未來的變更決定要付多少成本,會留給階段三。

明天,先從最小的 boundary:function 開始。


上一篇
Day12: Docs:只留下需要,而且仍然有效的文件
下一篇
Day14: Boundary:距離產生美感
系列文
AI時代下的軟體工程 共 18 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言