沒有安全棧的 AI 速度,最後會變成脆弱性
AI-generated code 最大的危險,不是它總是錯的。
真正危險的是,它經常「看起來已經對得足夠可以 merge」。
這正是風險所在。明顯有問題的 code 往往會被擋住。真正會進正式環境的是那種看起來合理、能過幾個 happy-path tests、卻悄悄削弱關鍵 boundary 的實作。
如果你的工作流只是 prompt、paste、review、merge,那麼生成速度每提升一次,系統變更速度和系統可信度之間的缺口就會再拉大一些。
解法不是繼續把希望押在 code review 的英雄主義上。解法是一套分層安全棧,在不同階段攔截不同類型的失敗。
第 1 層: 用 types、schemas、contracts 做 prevention
最便宜的 defect,是程式根本無法表達出來的 defect。
所以第一層是 prevention。
這一層的任務,是在 runtime behavior 發生之前先收緊 surface area:
- Branded types 和 phantom types 防止那些結構相似、語義不同的值被混用。
- Runtime schemas,例如 Zod,用來守住 untyped data 進入系統的邊界。
- Contracts 給關鍵 code paths 增加 preconditions、postconditions、invariants。
對於 AI-generated code,這一層尤其重要。模型很擅長生成「表面上自洽」的實作,但這種實作仍然可能犯下 category mistakes。如果你的 types 和 schemas 很弱,模型就有太大空間停留在「差不多對」的狀態。
第 2 層: 用 property-based tests 做 verification
Example tests 當然有價值,但它們太容易被人和模型一起 overfit。
模型完全可以同時生成一個 function 和一個看似匹配的 happy-path test。在 pull request 裡這看上去像生產力,實際上語義約束仍然很弱。
Property-based testing 改變的是問題本身。它不再問「這段程式在三個例子上能不能跑通」,而是問「在一整類 inputs 上,哪些性質必須始終成立」。
通常最值得先上的幾類 properties 是:
- round trips
- idempotence
- ordering invariants
- monotonicity
- invalid input 下正確的 error behavior
這裡 AI 其實很有幫助。模型通常能根據 signature 或 doc comment 給出一版不錯的 first-draft properties。人還是要 review,但 blank-page problem 會小很多。
第 3 層: 用 mutation testing 做 assessment
Coverage 不是品質指標,它只是執行指標。
Mutation testing 問的是更關鍵的問題: 如果程式碼以一種「看起來合理但其實錯誤」的方式發生變化,你的 tests 能發現嗎?
這就是為什麼 mutation testing 應該放在 contracts 和 property tests 之上。它不是替代前兩者,而是衡量前兩者是否真的有牙齒。
在 AI 時代,這一點尤其重要。模型可以生成一整套「看起來很強」的 tests,執行很多行程式碼,但真正斷言的東西很少。Mutation testing 會非常快地把這種 false confidence 揭出來。
實務上,不是要對所有程式碼時時刻刻跑 full mutation analysis。更現實的做法是:
- 先從 critical modules 開始
- 對 changed code 使用 incremental mutation testing
- 對 survivors 做嚴格 triage
- 隨著 suite 成熟逐步提高 thresholds
在 AI 時代,mutation testing 本質上是對抗假信心的解藥。
第 4 層: runtime containment 與 recovery
即使很強的 verification stack,也不可能抓住一切。
所以最外層一定要有 runtime containment。
這就是 crash-only design、deadlines、circuit breakers、leases、capability-based boundaries 開始發揮作用的地方。某個問題一旦漏過去,系統應該受控失敗,而不是把一次壞路徑放大成連鎖事故。
對很多團隊來說,這一層往往從更小的動作開始:
- 給 external calls 加 explicit timeouts
- 在會改 state 的 endpoints 上加 idempotency keys
- 給不穩定依賴加 circuit breakers
- 為敏感操作維持 narrow capability surfaces
目標不是完美,而是 bounded blast radius。
為什麼這些層疊在一起才真正有效
每一層抓住的都是不同類型的問題。
Types 和 contracts 先阻止明顯 invalid states。Property-based tests 檢查語義。Mutation testing 判斷 tests 是否真的有力度。Runtime containment 負責兜住仍然漏出去的部分。
核心思想是: 你不需要一個完美方法,你需要多個不完美但彼此獨立失效的層。
一個現實可落地的 rollout 長什麼樣
大多數團隊都不該試圖一次性全開。
更實際的順序通常是:
- 先收緊 TypeScript 或 Rust 的 boundaries
- 在外部輸入處加 runtime schemas
- 在關鍵 functions 上引入 contracts
- 為 serializers、reducers、validators 寫 property tests
- 在高風險模組上接入 incremental mutation testing
- 在最常出故障的 dependencies 周圍補上 runtime containment
這個順序之所以有效,是因為每一層都會強化下一層。更好的 schemas 會帶來更好的 properties。更好的 properties 會提高 mutation scores。Mutation feedback 又會反過來暴露 contracts 或 test depth 仍然薄弱的地方。
AI 時代工程實踐真正的分水嶺
最後贏的,不會是生成程式碼最多的團隊,而是能夠在不降低 trust 的前提下吸收更多 generated code 的團隊。
這正是安全棧要解決的問題。
它把 AI 從一個 speed amplifier 變成一個 reliability amplifier。模型負責幫助生成 implementations、tests、contracts、rules,而這套棧負責確保這些 artifacts 會被 deterministic systems 持續檢查,而不是僅僅因為「看起來不錯」就被信任。
如果你真的打算把 AI 用進 production engineering,這才是該追求的標準。不是再加一張 review checklist,也不是再多寫一句「be careful」。
而是一套分層系統,讓每一次 generated change 都必須經過 prevention、verification、assessment、containment。
這樣,快程式碼才會變成可信程式碼。