這篇文章整理以下幾個觀點:
核心想法是:
不要用相同的密度去閱讀所有 AI 生成的程式碼,而是先由人類設計出「應該遵守的規格」,再建立能從外部驗證它的狀態
這不是說 AI 寫的程式碼就不用看了,而是要透過設計與測試,讓人類的審查能集中在重要的判斷上。
※ 這是我在參加外部讀書會時學到的內容整理而成。
使用 AI 之後,可以比以前更短時間產生更多程式碼。
但是,如果這些程式碼都必須由人類逐行確認才能判斷是否正確,那麼人類端就會成為瓶頸,如下所示:
AI 大量撰寫程式碼
↓
人類大量閱讀程式碼
↓
審查跟不上
為了減少「必須把所有內部實作都讀完,才能理解行為」這種結構,本文會思考以下四件事:

契約式設計(Design by Contract)通常會用以下三種方式來整理處理流程的規格:
前置條件
→ 呼叫之前必須滿足的條件
後置條件
→ 在滿足前置條件並正常結束時成立的條件
不變條件
→ 必須持續維持的狀態
例如,假設有一個會變更數量的處理。
如果只是說「變更數量」,就無法知道是否允許負數、最大值是多少、超過上限時要怎麼處理。
因此,可以像下面這樣定義契約:
前置條件
→ 增加量必須是正整數
→ 變更前的數量 + 增加量 必須小於等於 20
後置條件
→ 正常結束後的數量 = 變更前的數量 + 增加量
不變條件
→ 數量永遠是 1~20 的整數
至於超過上限時,是要丟出例外,還是回傳代表失敗的結果,也需要先決定。
這樣一來,就不是只說:
請正確實作
而是先明確指出:
要滿足什麼條件才算正確
如果只是對 AI 說「請用契約式設計來實作」,契約本身也可能會變成由 AI 自行推測。
人類必須具體化哪些輸入是有效的、正常結束後狀態應該如何、哪些東西可以變更、哪些條件必須維持。
契約不一定只能寫在註解裡,也可以表現在型別、驗證、資料庫約束、測試等多個地方。
重點是:
要決定契約應該在哪裡表達、在哪裡強制
例如,假設有下面這個函式:
function calculateTotal(
prices: number[]
): number {
prices.sort((a, b) => a - b);
return prices.reduce(
(total, price) => total + price,
0
);
}
從呼叫端來看,這看起來像是一個會計算並回傳總和的函式。
但其內部其實透過 sort() 改變了傳入陣列的順序。
也就是說,這個函式實際上會做兩件事:
回傳總和
+
將傳入的陣列重新排序
如果這個副作用無法從外部得知,就必須讀到內部實作才能安全使用它。
因此,我們要問的是:
呼叫這個函式之後,除了回傳值之外,還有什麼會改變?
如果不想修改引數,就應該先複製再操作。甚至也可以重新檢討:計算總和真的需要排序嗎?
副作用本身並不是壞事。資料庫寫入、HTTP 通訊、記錄日誌,這些都是應用程式必要的副作用。
重點在於要讓副作用的位置清楚可見。
外層:取得輸入、驗證身分、DB、HTTP
↓
內層:業務規則計算
↓
外層:儲存、通知、記錄日誌
不是要把所有東西都變成純函式,而是:
先隔離副作用,再把能純化的部分盡量純化
例如,假設有以下程式碼:
const charge = CallCharge.create(
durationMinutes
);
只要使用端理解「根據通話時間建立通話費用」這個外部契約,就不必每次都追著內部的計算公式看。
好的抽象化,不只是把程式碼搬到另一個類別,而是:
建立一個能以外部契約為基準來使用的界線
不過,做了抽象化並不代表就能自動信任它。
在導入或變更時,仍然要審查內部實作,確認契約有被型別、約束、測試等機制所強制執行。正因為那個界線已被驗證且穩定,使用端才不需要每次都重看內部實作。
另外,也不是類別切得越細越好。應該抽象化的是那些具有獨特規則或不變條件,且值得為其命名的概念。
一旦決定了契約,就可以把它當成測試的依據。
例如,如果 Quantity 的不變條件是「1~20 的整數」,那就應該確認以下情況:
輸入期望結果1成功20成功0失敗21失敗1.5失敗重點不在於測試程式碼怎麼寫,而是要把契約和測試對應起來。
人類決定契約
↓
把契約轉成測試
↓
AI 實作
↓
驗證是否符合契約
不是先叫 AI「寫出正確的程式碼」,而是由人類先決定什麼才算正確。
即使測試通過了,也不代表整個系統的正確性已經被證明。
例如,仍然可能存在以下問題:
測試不可能自動證明所有輸入與狀態。
極端來說:
如果把錯誤的規格精準地測試出來,AI 也可能產生完全符合那個錯誤規格的程式碼
因此,在 AI 時代的審查中,必須設計出:
哪些部分可以機械式驗證,哪些部分應該集中人類判斷
在實作之前,可以先用以下問題來思考:
其中最重要的是:
要讓這段程式碼即使不用每次全部讀完,也能使用,那麼從外部可以確認哪些事情?
透過設計,減少「如果不把全部看完,就不知道會發生什麼事」的狀態。
當 AI 讓寫程式的速度變快時,
AI 實作
↓
人類用相同密度把全部都讀過一遍
這種方式很可能讓審查跟不上。
因此,我們要建立以下流程:
人類先決定應該守住的規格
↓
整理成前置條件、後置條件、不變條件
↓
隔離副作用
↓
抽象成有意義的單位
↓
以型別、資料庫約束、測試來表達
↓
交由 AI 實作
↓
驗證是否符合契約
↓
把人類審查集中在高風險部分
重點不是:
讓 AI 思考「正確的程式碼」
而是:
由人類先決定「滿足什麼條件才算正確」
這並不代表完全不用讀程式碼,而是要降低每次都用相同密度閱讀全部內容的必要性,讓人類的審查聚焦在真正需要判斷的地方。
契約、隔離副作用、抽象化、測試,都是 AI 出現之前就存在的基本設計原則。
正因為 AI 能產生的程式碼變多了,這些原則的重要性也更明顯地提高了。
原文出處:https://qiita.com/masashige0904/items/43beaaabc2c0ef2dcb1c