AI 寫出的程式能跑,為什麼還不能直接上正式環境

AI 寫出的程式「能跑」,只代表它在目前條件下完成了某個功能;要進正式環境,還要回答另一組問題:改動時會不會壞、錯誤能不能被發現、外部服務失敗時會怎樣、資料會不會出錯、程式是否容易維護,以及流量增加後是否仍然可靠。 從原型到正式產品,中間通常還缺一整套安全網、邊界與營運能力;繼續加功能並不能補上這些缺口。

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.”

元氣站長
元氣站長

經營網路事業,賺取持續收入!
網路賺錢修煉,成就理想人生!