讓 AI 幫你改程式之前,請先準備好測試
生產環境缺乏測試時,即使 AI 生成程式碼再完美,也難以確保系統行為不被意外破壞
前言
前幾天,有位工程師正在處理弱掃工具掃出來的問題,跑來問我應該怎麼改。
他的情境其實很常見:
🔎 系統裡已經有一段生產程式碼(Production Code),現在弱掃工具指出它可能有安全風險。問題是,這段程式沒有相關測試,所以他不太確定改完之後,行為是不是仍然正確,也不確定修改前後是否等價
這種情境下,最直覺的反應可能是:「既然弱掃說有問題,那就趕快改掉吧。」
但我反而會覺得,這時候最需要小心。
因為真正的風險不只是弱掃指出的問題,而是:
⚡ 我們在沒有測試保護的情況下,修改了一段正在生產環境運作的程式。
AI 可以協助分析,但不應該直接取代把關
💡 我當時給他的第一個建議是,可以先請 AI 協助評估這個問題。
例如讓 AI 幫忙看:
這個弱掃問題的本質是什麼?
它是真實風險,還是可能是誤報?
目前這段程式有哪些潛在問題?
有沒有比較小範圍、低風險的修改方式?
修改後可能會影響哪些行為? 這一步我覺得很適合交給 AI 協助。
✨ AI 很擅長快速閱讀程式碼、解釋風險、提出幾種可能的修改方向,也可以提醒我們一些容易忽略的邊界情境。
💥 但 AI 給出的修改建議就算看起來合理,也不代表它一定符合系統原本的行為。
尤其是既有系統裡,常常有很多隱含規則:
某些看起來奇怪的行為,其實是為了相容舊資料
某些條件判斷,是為了避開歷史問題
某些輸出格式,已經被其他系統依賴
某些例外情境,只有生產環境流量才會遇到 這些東西,AI 不一定知道。
所以我會把 AI 視為一個很強的協作者,但不是最後的保證者。
⚡ 真正能幫我們把關的,還是測試。
在改生產程式碼之前,先把現有行為固定下來
💡 如果一段生產程式碼沒有測試,我會傾向先補測試,再開始修改。
這裡的測試,不一定一開始就要寫得很完美,也不一定要馬上重新設計整個程式。
比較務實的做法是先寫特徵測試(Characterization Test)。
特徵測試的目的不是證明舊程式設計得很好,而是先記錄:
這段程式現在的行為是什麼?
輸入 A 時,目前會輸出什麼?
遇到邊界條件時,目前怎麼處理?
哪些行為是外部已經依賴的? 也就是先把「現在的樣子」固定下來。
這麼做的好處是,當我們接著讓 AI 協助修改程式時,就有一張安全網可以確認:
原本應該保留的行為有沒有被破壞?
新修改是否只影響預期範圍?
弱掃問題是否真的被處理?
有沒有引入新的副作用? 沒有測試時,我們只能靠感覺判斷改得對不對。
有測試時,我們至少有一個可以被執行、被驗證的基準。
一個簡單的例子:從弱掃問題看 AI 協作修改
假設系統裡有一段既有程式:
public string GetUserDisplayName(string name)
{
return $"<div>{name}</div>";
} 這段程式會把使用者名稱包在 HTML 的 div 裡。
如果弱掃工具指出這裡可能有 XSS 風險,原因是使用者輸入被直接輸出到 HTML 中。
例如:
GetUserDisplayName("<script>alert(1)</script>"); 可能會產生:
<div>
<script>
alert(1);
</script>
</div> 這確實是一個需要處理的問題。
這時候我們可以請 AI 協助分析,AI 很可能會建議使用 HTML Encode:
public string GetUserDisplayName(string name)
{
return $"<div>{HttpUtility.HtmlEncode(name)}</div>";
} 這個修改方向看起來合理。
但在真正修改之前,我們應該先補上現有行為的測試。
XSS 是一種常見的安全漏洞,攻擊者可以透過注入惡意的 JavaScript 程式碼來竊取使用者資料或執行不當操作。弱掃工具會檢測到這種風險,建議開發者對輸入進行適當的處理,例如 HTML Encode 以防止惡意程式碼被執行
先補現有行為測試
例如先寫一個測試,確認原本的主要行為:
[Fact]
public void GetUserDisplayName_ShouldWrapNameWithDiv()
{
var service = new UserService();
var result = service.GetUserDisplayName("Neil");
Assert.Equal("<div>Neil</div>", result);
} 這個測試看起來很簡單,但它有一個重要目的:
它先幫我們固定了目前生產程式碼的基本行為。
也就是:
輸入一個一般名稱
系統會把它包進 div
輸出格式是 <div>{name}</div> 接著,我們再補上安全相關的測試:
[Fact]
public void GetUserDisplayName_ShouldEncodeHtmlInput()
{
var service = new UserService();
var result = service.GetUserDisplayName("<script>alert(1)</script>");
Assert.Equal(
"<div><script>alert(1)</script></div>",
result
);
} 這個測試則是在描述新的安全需求:
如果輸入包含 HTML 或 Script
輸出時應該被 Encode
不能直接原樣輸出到 HTML 裡 有了這兩種測試後,再進行修改就會安全很多。
因為我們同時確認了兩件事:
原本包 div 的行為仍然存在
新的安全需求也被滿足 AI 協作時,測試是最重要的邊界
這個例子雖然是弱掃問題,但我真正想帶出的不是「弱掃要怎麼修」。
更重要的是:
✨ 只要是透過 AI 協作修改既有程式,都應該要有測試把關。
因為 AI 產生程式碼的速度很快,也常常能提出看似合理的解法。
但越是這樣,我們越需要一個客觀機制來驗證它。
不然很容易發生這種情況:
AI 改掉了弱掃問題,但也改壞了原本行為
AI 重構了程式,但刪掉了某個隱含規則
AI 讓程式看起來更乾淨,但破壞了相容性
AI 通過了表面案例,但漏掉邊界條件 💥 這些問題不一定是 AI 不好,而是因為它缺少完整的系統脈絡。
⚡ 測試的角色,就是把這些脈絡盡量明文化。
建議流程
如果要讓 AI 協助修改生產程式碼,我會建議流程大概是這樣:
1. 先請 AI 協助分析問題
2. 確認修改方向是否合理
3. 補上現有行為的特徵測試
4. 補上新需求或安全情境的測試
5. 再讓 AI 協助修改程式
6. 跑測試確認行為沒有被破壞
7. 再次檢視(Review)AI 產生的程式碼
8. 必要時補更多邊界案例 ✨ 這個流程的重點不是讓開發變慢,而是讓修改變得可控。
AI 可以加速我們理解問題、撰寫測試、產生修改方案。
但測試可以幫我們確認,這些加速沒有把系統推向未知風險。
結語
AI 讓修改程式變得比以前容易很多。
但也正因為修改變容易了,我們更需要小心:容易修改,不代表容易改對。
尤其是面對生產程式碼、遺留程式碼,或任何缺乏測試保護的程式時,我們不應該只是把 AI 產生的修改直接套上去。
👍 比較好的做法是,先用測試固定現有行為,再讓 AI 協助修改,最後用測試確認結果。
AI 可以是很好的協作者。
但在軟體開發裡,真正讓我們安心前進的,仍然是可以被反覆執行的驗證機制。
✨ 也就是測試。
所以,讓 AI 幫你改程式之前,請先準備好測試
