AI 很會把需求快速變成可以操作的功能。
你描述一個表單,它能寫出表單。
你要會員登入,它能很快拼出登入流程。
你要串金流、寄信、呼叫 API,它也常常能在短時間內做出第一版。
這種速度非常有價值。
但「我在自己電腦測過,可以跑」和「我敢讓真實客戶、真實付款與真實資料使用」中間,還隔著一大段工程工作。
如果直接把能跑的原型當成正式產品,風險通常不是第一天就爆炸。
更常見的是幾週後開始出現:改一個地方壞另一個地方、某個外部服務變慢整站一起卡住、資料重複寫入、錯誤無法追查,最後沒有人敢再碰程式。
第一步:先建立安全網,再談重構
沒有測試的程式,最大的問題不是「寫得醜」。
而是你不知道修改後,原本哪些行為被你一起改掉了。
Michael Feathers 在處理既有程式碼的方法裡,很重視先把目前行為固定下來。
這類測試常被稱為特徵測試(Characterization Test)。
這類測試先把程式目前的實際行為記錄下來,後面才有安全修改的基準。
例如:
會員註冊成功後會建立哪些資料?
付款完成後訂單狀態怎麼變?
折扣碼在什麼條件下生效?
權限檢查會擋哪些角色?
把這些關鍵行為先釘住,後面才有資格大改結構。
測試先覆蓋高風險改動點,不必一開始追求百分之百
剛開始補測試時,不需要立刻追求所有程式碼都有測試。
可以先找三種地方:
下一個版本一定會改的地方。
最常被改、最容易壞的地方。
一壞就會造成重大損失的核心邏輯。
例如金流、權限、訂單狀態、會員資料、重要計算,就比一個很少使用的後台裝飾元件更值得先測。
測試的目的,是讓你敢改最重要的地方,而不是讓覆蓋率數字看起來漂亮。
第二步:程式可讀,才有辦法安全交給下一個人
AI 很容易產生「當下看得懂,但幾天後不想碰」的程式。
常見情況包括:
函式太長。
一個函式同時查資料、判斷商業規則、寄信又改狀態。
變數叫 data、temp、result,讀完還是不知道代表什麼。
同一段規則在不同檔案各寫一份。
錯誤被一個大範圍 catch 全部吃掉。
可讀性不是潔癖。
它會直接影響下一次修改需要多少理解成本,以及你有多容易誤改。
如果想深入看這一層,可以延伸閱讀 Clean Code:好程式碼不是能跑就好,而是讓下一個人看得懂、改得動。
第三步:重構要和改功能分開
重構(Refactoring)的核心,是改善內部結構,同時維持外部行為不變。
Martin Fowler 長期強調小步驟與行為保持。
實務上很重要的一個原則是:
先讓測試是綠的。
做一個小型結構調整。
再跑測試。
確認仍然是綠的。
接著才做下一步。
如果你一邊改架構、一邊加新功能、一邊順便修錯誤,最後測試失敗時,很難知道是哪一個動作造成的。
所以「整理結構」和「改變行為」最好分開。
看到錯誤時,不要順手全部一起修
在替舊程式補測試時,可能會發現一些奇怪行為。
直覺會想:「這明明是 Bug,順便修掉。」
但正式系統裡,有時候別的流程已經依賴這個行為。
比較安全的方式是先把目前行為測起來,記錄成技術債,再用獨立改動處理。
這樣你知道哪一次提交是在固定行為,哪一次是在改行為。
第四步:商業規則不要被框架和資料庫綁死
快速原型很常把所有東西直接寫在控制器、頁面元件或資料庫模型裡。
短期很快。
之後卻會變成:
要測折扣規則,必須先啟動整個框架。
要換付款服務,核心流程也跟著大改。
要換資料庫,商業邏輯裡到處都是 ORM 呼叫。
比較健康的方向,是讓「訂單怎麼計價」「會員能不能存取」「什麼狀況可以退款」這些商業規則,盡量不要依賴外部框架。
框架、資料庫、金流、寄信服務,都應該比較像外層可以替換的工具。
這也是 Clean Architecture:為什麼商業邏輯不該被框架與資料庫綁死 最核心的觀念之一。
第五步:外部服務一定會有失敗的時候
本機測試時,第三方 API 可能每次都很快。
正式環境卻要假設:
對方偶爾會逾時。
網路可能斷掉。
服務可能回傳錯誤。
同一個請求可能被重送。
依賴服務可能整段故障。
AWS Well-Architected 的可靠性建議明確要求遠端呼叫設定合理的連線與請求逾時,而不是無限等待預設值。
因為一個變慢的外部服務,可能占住你的執行緒、連線池或背景工作處理資源,最後把原本健康的系統一起拖垮。
逾時、重試與退避要一起設計
外部服務失敗時,「再試一次」有時有用。
但所有請求同一時間瘋狂重試,也可能把對方打得更慘。
比較成熟的重試會搭配:
明確的逾時。
有限重試次數。
指數退避(Exponential Backoff)。
抖動(Jitter),讓重試時間分散。
而且不是每一種操作都能安全重試。
例如付款或建立訂單,如果沒有冪等性(Idempotency)設計,重試可能造成重複扣款或重複建立資料。
第六步:用斷路器避免一個故障拖垮全部
斷路器(Circuit Breaker)適合用在重要外部依賴。
當失敗連續超過門檻時,系統暫時停止繼續呼叫,直接快速失敗。
過一段時間後,再用少量請求測試服務是否恢復。
這個設計的價值,是不要讓你的系統一直把資源消耗在一個明顯壞掉的依賴上。
同樣地,重要依賴也可以用資源隔離(Bulkhead)限制它最多消耗多少連線或工作執行緒。
第七步:正式環境前要補上安全邊界
AI 快速產生功能時,很容易把「能操作」誤當成「安全」。
正式環境至少要重新檢查:
每一個敏感操作都有沒有做權限驗證。
輸入資料有沒有檢查格式、範圍與商業意義。
API 金鑰、資料庫密碼與憑證有沒有寫死在程式碼裡。
不同環境的秘密金鑰有沒有分開管理。
日誌裡會不會把密碼、Token 或其他敏感資料直接寫出去。
OWASP 對秘密管理的建議很明確:秘密應該集中管理、限制存取、定期輪替,而且遵守最小權限。
權限檢查也不能只做在前端按鈕上;每一個真正執行敏感操作的請求,都要在伺服器端重新驗證。
輸入驗證則要同時看格式和語意,例如日期格式正確,並不代表結束日期早於開始日期就是合理資料。
第八步:正式環境要能被觀察
原型只需要「我自己看到結果」。
正式產品需要回答:
現在有沒有錯?
錯在哪裡?
發生多久了?
影響多少人?
哪個版本開始發生?
系統現在還能不能服務使用者?
所以至少要有:
結構化日誌。
錯誤追蹤。
重要指標。
健康檢查。
延遲與錯誤率監控。
正式環境更棘手的是:問題發生後,你多久才知道、多久才找得到原因。
第八步:部署和發布最好分開
把程式部署上伺服器,不代表所有人立刻都要看到新功能。
功能旗標(Feature Flag)可以把「程式已部署」和「功能已對使用者開放」分開。
這讓你可以:
先只開給內部測試。
再開給少量使用者。
發現問題時先關功能,不必等重新部署。
資料庫變更也要能前後相容。
正式環境最怕的是新程式一上線,舊資料、新資料、舊版本全部互相不相容。
第九步:先算容量,再決定要不要「上大架構」
AI 很容易幫你生出微服務、訊息佇列、快取、讀取副本、分片等架構。
問題是你真的需要嗎?
如果現在每天只有幾十位使用者,先做大量分散式架構,可能只是把系統變難維護。
比較好的順序是先估算:
每日活躍使用者。
每位使用者每天會做幾次核心操作。
平均與尖峰請求量。
資料每天增加多少。
一年後大概多少容量。
先知道瓶頸可能出現在哪裡,再決定要不要加快取、佇列、讀取副本或分片。
第十步:資料正確性比畫面成功更重要
前端顯示「成功」,不代表資料層一定安全。
想像兩個人同時購買最後一個名額。
兩個請求都先讀到「還有 1 個」。
接著都扣掉 1。
如果沒有適當的鎖定或交易隔離,兩個人都可能成功。
這種併發問題,在單人本機測試幾乎看不到。
所以正式環境還需要理解:
資料庫隔離等級。
交易。
唯一性約束。
鎖定。
重試時的冪等性。
資料備份與復原。
AI 產生的程式碼,最需要的是「驗證流程」
問題不在 AI 能不能寫好程式。
AI 可以寫出很好,也可以寫出很糟的程式。
關鍵在於團隊有沒有一套固定的驗證方法。
每次 AI 修改後:
測試有沒有跑?
有沒有改到不該改的行為?
錯誤處理有沒有被吃掉?
外部呼叫有沒有逾時?
敏感資料有沒有被寫進日誌?
資料庫操作在併發下安全嗎?
這些問題不應該靠「看起來沒問題」決定。
從原型到正式產品,可以用這個順序
如果現在手上是一個 AI 快速做出的原型,可以依這個順序處理。
一、先替核心行為建立特徵測試。
二、整理最難讀、最常改的程式。
三、用小步驟重構,不要和新功能混在一起。
四、把商業規則從框架與資料庫細節拉開。
五、檢查所有外部服務的逾時、重試與故障隔離。
六、建立日誌、指標、健康檢查與告警。
七、把部署和功能發布分開。
八、用實際流量估算容量,不要先過度設計。
九、檢查資料交易、併發與復原能力。
這個順序的重點,是先讓程式「敢改」,再讓它「好改」,最後才讓它「扛得住正式環境」。
能跑只是第一關,敢改、敢上線、敢營運才算完成
AI 讓第一版產品的速度大幅提高。
這是好事。
但第一版越快,後面的品質流程反而越重要。
正式產品需要的不只是功能正確。
它還要可測試、可理解、可修改、可觀察、可復原,也要能在依賴失敗與多人同時操作時維持可接受的行為。
如果你的原型已經開始有真實使用者,最優先的工作通常不是再加十個功能。
先建立安全網,讓每一次之後的修改都能更放心。
常見問題
AI 寫的程式是不是都不適合正式上線?
不是。來源不是判斷品質的唯一標準。人工寫的程式也可能很差,AI 寫的程式也可能很好。關鍵是有沒有經過測試、審查、風險檢查與正式環境設計。
原型完全沒有測試,第一步應該測什麼?
先測即將修改、最常變動,以及一壞就會造成重大損失的核心行為,例如登入、權限、付款、訂單與重要商業規則。
重構時可以順便加功能嗎?
不建議混在同一步。重構應盡量保持外部行為不變,先在測試綠燈下完成小型結構調整;新功能再用獨立改動加入,比較容易定位問題。
小型網站也需要斷路器嗎?
不是所有外部依賴都要一開始做到完整斷路器,但逾時是基本要求。對付款、重要第三方 API 等關鍵依賴,如果失敗會大量占用資源或造成連鎖問題,就值得加入斷路器與隔離設計。
什麼時候才需要考慮大規模架構?
先根據實際使用者、請求量、尖峰、資料量與成長速度估算。當單機、快取或簡單資料庫優化已無法滿足需求,再逐步增加更複雜的架構,通常比一開始就設計大型分散式系統安全。
參考資料
Michael Feathers. Working Effectively with Legacy Code.
Martin Fowler. Refactoring: Improving the Design of Existing Code, Second Edition.
Robert C. Martin. Clean Code: A Handbook of Agile Software Craftsmanship.
Robert C. Martin. Clean Architecture: A Craftsman’s Guide to Software Structure and Design.
John Ousterhout. A Philosophy of Software Design, Second Edition.
David Thomas and Andrew Hunt. The Pragmatic Programmer: Your Journey to Mastery.
Michael T. Nygard. Release It! Design and Deploy Production-Ready Software, Second Edition.
Martin Kleppmann. Designing Data-Intensive Applications.
Amazon Builders’ Library. “Timeouts, retries, and backoff with jitter.”
Martin Fowler. “Circuit Breaker.”
