iT邦幫忙

2026 iThome 鐵人賽

DAY 22
0
Software Development

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

Day22: 階段二回顧,與階段三的起點

  • 分享至 

  • xImage
  •  

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

階段二想解決什麼:Contain

系統只要有一定的規模,就大到沒有人能一次理解全部;coding agent 的 context 再大,也是如此。所以 Day14 一開始問的是:為了改這一小段程式,我們到底需要知道多少其他東西?

階段二的答案是 Contain:用 boundary 把一次 change 圈住。要圈住的範圍有兩個,Day17 與 Day21 都提過:

  • 理解範圍:完成這次 change,需要讀多少東西。
  • 影響範圍:這次 change,會擴散到哪裡。

兩個範圍都夠小,我們才能只看局部、也只改局部,這就是 local reasoning。

回頭看這八天,其實是沿著同一條線,一步一步把 boundary 守起來:

主題 守住了什麼
立起一扇門 Boundary(Day14) 分出裡與外,client 不必知道裡面怎麼做
Contract(Day15) 把兩側的約定寫下來:spec 給 client,test 驗 implementer
Abstraction(Day16) 把 contract 從實作中抽出來,寫得進型別的交給工具
堵住側門 Dependency(Day17) 只依賴完成工作所需要的資訊
State & Side Effect(Day18) 讓隱藏的輸入與輸出看得見,並集中在少數地方
Representation Invariant(Day19) 門後的資料,由 implementer 全權負責
連成一張圖 Module(Day20) 一個 module,對外只留一扇門
Dependency Graph(Day21) 依賴的箭頭有方向,但不回頭

前三天,是把一扇門立起來。Boundary 把程式分成裡與外,client 只需要知道怎麼用,implementer 才需要知道怎麼做;contract 把兩側的約定寫下來,改動只要沒碰到 contract,就留在 implementer 這一側;abstraction 再把 contract 從實作中抽出來,讓 type checker 替兩側把關。

中間三天,處理的是「門立好了,change 卻不走門」的情況。Dependency 是 change 傳播的路徑,依賴了不需要的資訊,路就變多了;state 與 side effect 是 signature 上看不見的輸入與輸出,讓 dependency 繞過了 contract;而門後的資料若沒有 invariant 守著,或是直接交到 client 手上,裡面也就不再由 implementer 說了算。

最後兩天,把同一件事放大到整個 system。Module 從一整群 class 與 function 裡,挑出能跨出去的名稱,對外只留一扇門;dependency graph 則把這些門連起來,箭頭有方向、不回頭,兩個範圍才有盡頭。

劃線的是人,守線的是工具

這八天有一個反覆出現的模式:先由人決定線在哪裡,把它寫下來,再交給工具守住。寫得進型別的交給 type checker,invariant 寫成檢查,module 的界線與依賴的方向交給 compiler 或 linter。這和階段一是同一件事:把原本只是提醒的東西,變成驗證。

但工具只能守線,不能替我們劃線。因此,階段二中人的角色是:建立 boundary,並且讓它守得住。

具備這個能力之後,與 coding agent 合作的方式也會跟著改變:

  • 交代任務時,說得出分工:除了「做出這個功能」,還能說明「只透過 Storage 存取帳目」、「這個 module 只公開這幾個名稱」、「pi-ai 不能 import pi-agent-core」。
  • 檢查結果時,知道該看哪裡:不必從第一行讀到最後一行,而是先問幾個問題。它改的是門後,還是門本身?有沒有從側門進去?signature 之外,多讀或多改了什麼?dependency graph 上,多了哪一條箭頭?
  • 越線時,讓 agent 自己發現:線一旦寫成工具能檢查的規則,agent 越線時就會收到明確的錯誤,可以自行修正,不必等到 code review。

前兩件事方向相反,一個是向 AI 指定結構,一個是讀懂 AI 產出的結構,用的卻是同一組詞彙。而 boundary 守得住,agent 自己也受益:使用別人的 code 時能少讀一點,寫自己的 code 時也知道該守住什麼。

階段二的 scope

階段二回答的是「boundary 如何守得住」。但回頭看這八天的例子:Storage、Square、storage module、pi 的四個 module,boundary 都是一開始就給定的。我們只問它守不守得住,從來沒有問:為什麼線是劃在這裡?

Day13 留下過兩個例子:同一條折扣規則散落在三個 module 裡;一個 class 同時負責計價、庫存與通知。用階段二的標準檢查,它們可以全部過關:每個 module 都只留一扇門,contract 寫得清楚,dependency graph 也沒有環。但折扣規則一改,還是得改三個地方;想改計價,還是得先理解庫存與通知。

Boundary 守住了,卻劃錯了位置。 該放在一起的被拆開,不該放在一起的被關在同一扇門後。理解範圍與影響範圍依然很大,卻沒有任何工具會提醒我們。

其他幾天也留下了類似的問題:

  • Day14:SQLite 換成 PostgreSQL,change 留在 boundary 裡;改成 remote API,change 卻穿了過去。Day15 的解法,是一開始就把「儲存可能失敗」寫進 contract,但這等於在設計的時候,就先猜到了未來的 change。
  • Day16:什麼時候該抽出 abstraction?當時的答案是「同一件事已經有、或很快會有不只一種做法」。但「很快會有」,要怎麼判斷?
  • Day21:共用的東西,要在下層匯合成一個點。但兩段今天長得一樣的 code,明天還會一樣嗎?

這些問題的共同點是:答案取決於未來會怎麼變。 而這正是階段二沒有處理的事。

變便宜的,與沒有變便宜的

過去,boundary 有一道天然的煞車:建立它很花力氣。抽一個 abstract class、拆一個 module、搬檔案、改 import,每一步都要人動手;不值得的 boundary,往往在動手之前就被放棄了。

coding agent 把這道煞車拿掉了。它能在很短的時間內交出一層又一層的抽象,而且 type check 通過、test 通過,每一層都有清楚的 contract。

假設系統只串接了一家金流,agent 卻可能寫出:

IPaymentGateway → PaymentGatewayFactory → StripePaymentGatewayAdapter → StripeClientWrapper → Stripe

用階段一的標準看,它全部通過;用階段二的標準看,boundary 清楚、依賴有方向、沒有環。但它仍然可能是不好的設計:為了一個還不存在的 change,現在就先付了 complexity。

因為變便宜的,只有「把 boundary 寫出來」這件事。Boundary 存在之後的成本,一點都沒有少:

  • 每多一層,就多一個名稱要維護,多一扇門要讀。
  • 每多一份 contract,就多一個不能隨意更動的承諾;指向它的箭頭越多,越難改。
  • 劃錯位置的 boundary,往往比沒有 boundary 更難修正,因為已經有人依賴它了。

階段三:Choose

所以,下一個階段要回答的問題是:boundary 該放在哪裡?又值得為未來的 change,先付多少成本?

這個問題難在,未來的需求沒有人知道。劃得太少,change 來的時候到處都要改;劃得太多,每天都在為沒有發生的事付出代價。階段三的名字是 Choose:每一次 change 都有成本,而設計,就是決定把成本付在哪裡。

SOLID 這類設計原則會在這個階段登場,但不是當成必須遵守的戒律。它們回答的其實是同一個問題:哪一種結構,讓哪一類 change 變得比較便宜? 而每一個答案,都得把 abstraction 本身的成本一起算進去。

把三個階段放在一起看:

階段一:Control 階段二:Contain 階段三:Choose
消不掉的限制 程式可能做錯 系統大到無法一次理解 未來的需求無法預測
問題 如何控制 AI 做出的一次 change? 如何限制一次 change 需要理解與影響的範圍? boundary 該放在哪?值得先付多少成本?
人的角色 定義 constraints 建立並守住 boundaries 決定 boundary 的位置與 trade-offs

階段一與階段二,最後大多能交給工具:constraint 可以執行,boundary 可以檢查。階段三不行。沒有任何 linter 能告訴我們,這條 boundary 值不值得存在;這需要的是對需求與未來的判斷,而這個判斷,終究要由人來負責。

階段二教會我們怎麼守住一條線。接下來要學的,是怎麼選擇把線劃在哪裡。


上一篇
Day21: Dependency Graph:一張不必讀實作的地圖
下一篇
Day23: Cohesion:會一起變的,放在一起
系列文
AI時代下的軟體工程 共 24 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言