為什麼 GitHub Copilot 生成的程式碼錯誤難以歸因

行內補全的風險樣貌,和會改寫整份檔案的代理程式不同。每一則建議都小到讓人覺得可以審閱,於是你花一秒看過就往下走。那一秒的注意力,對眼前這一行是誠實的,對周圍的系統卻是盲目的。

結果就是,當某個地方壞掉時,用二分法逐步回溯會非常折磨。沒有單一可疑的 commit,只有一長串被接受的補全,每一則在當時看起來都正確。

三種值得留意的偏移

慣例偏移

  • 補全沿用的是它訓練時學到的模式,以及附近程式碼的模式,而那不一定是你專案實際採用的慣例。

  • 錯誤處理、null 檢查與日誌記錄,會在各模組之間慢慢分歧。

重複的邏輯

  • 接受一個生成的輔助函式,比去翻出既有的那一個更快,於是同一條規則最後在三個地方各被實作了一次。

  • 後來其中一處被修好了,另外兩處沒有。

看似合理的預設值

  • 被建議的預設值、逾時設定或排序方式看起來很合理,卻不符合你產品的規則。

  • 什麼都沒有失敗。只是這個行為,並不是任何人原本想要的那一個。

這三種偏移在審閱時都看不見,要等產品實際執行才看得見——而這也決定了檢查該放在哪裡。

按節奏檢查行為,而不是逐一檢查補全

驗證每一則被接受的建議既不可能,也沒有用。正確的單位是 Pull Request:到那時已經累積了一批完整的變更,卻又還小到出問題時推敲得出原因。

你要檢查的不是新寫的程式碼,而是這段程式碼所處的行為。動到發票模組的 Pull Request,就該把開立發票從頭到尾跑過一遍,包括作者沒想到的那些路徑,因為看似合理的預設值正是躲在那裡。

安裝驗證 skill,代理程式就會自己做這件事,不必等某個人想起來。

終端機

npm install -g @testsprite/testsprite-cli
testsprite setup

不想在本機安裝的人,儀表板也涵蓋同樣的範圍;完整的指令介面則放在 CLI 儲存庫。

沒人問得夠早的回歸問題

對一個大量依賴補全的專案,有用的問題不是「這段程式碼好不好」,而是「產品是否還在做它上個月做的事」。這是兩個不同的問題,而只有後者抓得到偏移。

要回答它,需要一組早於這次變更就存在的檢查,而這正是團隊會一直往後拖的部分。第一週寫下的十條流程,比第一次事故之後才寫的一百條更有價值,因為只有最早那十條,是在還沒人知道哪些會出事之前就定義好的。

能夠長久維持的審閱習慣

審閱每一則補全不可能,一則都不審閱則正是偏移累積的原因。真正行得通的習慣,是按類別審閱,而不是逐行審閱。

當一則補全引入了錯誤處理路徑,檢查它是否和模組其他地方處理錯誤的方式一致,因為這裡的分歧是最常見的偏移,也是日後最難拆解的。當它引入了預設值,問一下這個預設值是哪來的,因為看似合理的預設值是改變行為最安靜的方式。當它引入了輔助函式,花十秒找一下既有的那一個,因為重複的邏輯會讓後來的修正做不完整。

這三項檢查各只要幾秒鐘,就能攔下大部分會累積起來的問題。其餘的,用實際執行產品來抓,會比用眼睛讀更有效。

讓它自動進行

偏移是漸進的,所以檢查必須規律到近乎無聊。任何依賴「有人記得」的做法,都會剛好在最忙、也接受最多補全的那一週被跳過。

如果 pipeline 屬於另一個團隊, GitHub App 就是阻力最小的路徑:它是一個 webhook,不會改動你儲存庫裡的任何東西,並在你的建置回報新版本已上線時觸發。如果你希望這個檢查直接在儲存庫裡看得見,一個 GitHub Actions 步驟就能做到。

不必讀完每一則補全也能抓到偏移

審閱每一則被接受的建議並不可能,所以 TestSprite 改為檢查程式碼所處的行為。測試覆蓋由你的產品生成,在每一個 Pull Request 上執行,並回答對大量依賴補全的專案而言真正重要的問題:它還在做上個月做的事嗎。

這樣就能直接抓到那三種偏移。改掉排序方式的看似合理的預設值,會表現為某條流程行為不同。重複的邏輯會在其中一份被修好、另一份沒有時顯現。錯誤處理上的慣例偏移,會表現為某條路徑現在安靜地失敗了。

它就在變更發生的地方執行,以 Pull Request 留言或 commit 檢查的形式出現,所以一個回歸問題指向的是一批補全,而不是一整季的補全。

Copilot 生成的程式碼比手寫的更差嗎?

多數情況下,就單行來看並沒有更差。風險在於量:每小時進入儲存庫的程式碼,比審閱流程當初設計時能承受的更多,所以同樣的缺陷率會讓更多問題溜過去。

更嚴格的審閱能解決這件事嗎?

有一部分可以,但這和大家一開始使用補全的理由互相衝突。把檢查從「讀程式碼」移到「驗證行為」,既保住速度,也把安全網補回來。

那 Copilot 寫的測試呢?

對覆蓋率有幫助,但作為獨立的檢查很弱。從與程式碼相同的假設生成出來的測試,本質上必然會與程式碼一致。

我們該先涵蓋多少條流程?

先從你會拿去向客戶示範的那幾條開始,再加上任何牽涉金流或權限的路徑。之後依事故來擴充,而不是照著覆蓋率目標擴充。

這需要存取儲存庫嗎?

驗證是透過介面,對已部署的應用程式執行。存取儲存庫只用於把結果回報到 Pull Request 與 commit 上。

簡短版

問題出在偏移,不在那一則建議。

GitHub Copilot 生成的程式碼錯誤,會在許多被接受的小型補全之間累積。以 Pull Request 為單位檢查行為,而不是以單一建議為單位;在你需要之前就先定義好流程;並讓檢查自動持續執行。