跳到主要內容
maxstudioNews
AI 自動化DEV Community (Claude Code)約 9 分鐘

AI 寫的程式碼,你敢信任嗎?一套驗證流程確保品質

當 AI 程式碼代理完成功能實作並報告成功時,開發者仍需警惕,因為生成可運作的程式碼與驗證其行為是否正確是兩回事。本文作者提出一套多層次的驗證工作流程,從可觀察的驗收標準開始,並在多個層次檢查實作,以彌合 AI 代理聲稱完成與實際產品需求之間的鴻溝。

AI 寫的程式碼,你敢信任嗎?一套驗證流程確保品質
圖片來源:DEV Community (Claude Code)

我是 Tony,我負責開發和營運網路產品,而 Claude Code 是我主要的開發工具。當一個程式碼代理完成功能實作,報告一切就緒並提供變更摘要時,程式碼看起來合理,測試也顯示通過,但你還有其他任務等著。

然而,這個代理到底證明了什麼?生成可運作的程式碼與驗證其行為是否正確,是兩種不同的工程活動。我採用一套驗證工作流程,從可觀察的驗收標準開始,在多個層次檢查實作,並在接受變更前記錄證據。

我將以一個帶有管理介面和多租戶 API 的 TypeScript 應用程式為例,其中包含一個普通測試可能遺漏的授權錯誤,來詳細說明這個流程。你將會獲得一份可重複使用的驗證清單,以及一個完整的範例,展示可接受的證據鏈是什麼樣子。

一個程式碼代理可以產生令人信服的實作,但卻無法證明它解決了原始問題。它可能修改了正確的檔案,遵循了現有的模式,並成功執行了可用的測試。然而,這些結果都不能自動證明所請求的行為是有效的,它們只回答了更狹隘的問題。

考慮代理可能提出的四種陳述:「實作完成」代表代理產生了它認為已完成的程式碼;「TypeScript 編譯通過」表示編譯器沒有發現該檢查涵蓋的錯誤;「所有測試通過」表示在配置條件下執行的測試都通過了。

但「所請求的行為有效」則需要證據來證明實作滿足了驗收標準。這種差異很重要,因為測試只會練習有人預期的情境。

一個通過的授權測試可能證明資源所有者可以存取文件,但對於另一個授權成員或不相關的租戶卻隻字未提。成功的瀏覽器互動可能顯示文字出現在編輯器中,但無法證明正確的值已儲存。

即使是有效的下載,也可能包含權限不正確的檔案。我將此視為一個驗證鴻溝:代理的最終訊息位於一側,而支持產品需求的證據則位於另一側。

我的職責是彌合這個鴻溝,而不僅僅是判斷生成的程式碼看起來是否合理。同樣的區別也適用於人類編寫的程式碼,但代理改變了開發人員在不親自追蹤每個決策的情況下可以產生的實作數量,這使得嚴謹的驗收流程變得更加重要。

在實作之前,我需要一份簡短的合約,描述系統必須做什麼以及我們將如何知道它有效。這不是一份完整的規格說明或詳細的測試計畫,而是一組可獨立於代理解釋進行檢查的可觀察行為。

想像一個 pnpm TypeScript monorepo,其結構如下:apps/admin/ (React 管理應用程式)、api/ (多租戶 API)、packages/ui/ (共享 UI 元件)、shared/ (共享合約和工具)。

假設 API 為多個客戶組織提供服務,一個資源可以與多個群組關聯,而使用者透過群組成員資格獲得存取權限。請求的變更是修正一個錯誤地拒絕某些授權使用者的權限檢查。

這是我會給代理的驗收合約:任務:修正基於群組的共享資源存取。預期行為:1. 屬於資源所附加的任何群組成員,可以在同一租戶內存取該資源。2. 不屬於所有附加群組的使用者將被拒絕。3. 來自其他租戶的使用者將被拒絕,即使群組識別碼重疊。4. 現有的資源所有者存取權限保持不變。

所需證據:針對所有四種情況的重點授權測試;一個回歸測試,其中授權使用者屬於第二個附加群組;確認回歸測試在沒有修正的情況下失敗,並在修正後通過;變更檔案和剩餘風險的摘要。

限制:保留租戶隔離;不要變更不相關的授權行為;不要僅為了讓測試通過而修改測試。

這份合約指定了結果而非實作細節。我沒有指示代理使用特定的 SQL 查詢、ORM 方法或函數結構。它可以選擇適合現有程式碼的方法,但不能將成功完成重新定義為「程式碼編譯通過」。

邊緣案例具有很大的價值。當存取權限取決於多個關係時,僅測試所有者和未經授權的陌生人是不夠的。重要的案例是屬於有效群組但先前實作未能考慮到的使用者。

對於錯誤修正,我還會提供重現指令、相關的堆疊追蹤(如果存在),以及重現行為所需的步驟。我會說明故障是否間歇性發生。這些細節減少了猜測,並為代理提供了一個具體的起點。

原則很簡單:在評估實作之前定義成功。否則,代理的解決方案可能會影響每個人認為可接受的測試。

我將實作指令與驗收決策分開。代理可以檢查儲存庫、提出解決方案、編輯程式碼並執行測試。它還可以識別遺漏的案例並解釋技術權衡。但我不會將其最終評估視為獨立證據。

對於範圍不明確的變更,我會以計畫模式啟動 Claude Code:claude --permission-mode plan。計畫模式允許 Claude 讀取專案並提出方法,在獲得批准之前不會編輯檔案。

我利用這個界限來檢查假設、識別受影響的模組,並檢查提議的測試是否涵蓋了驗收合約。計畫很有用,因為它在實作使這些決策更難被注意到之前就暴露了決策。

對於授權範例,我可能會使用這個提示詞:「檢查共享資源授權流程。識別:資源如何與群組關聯;租戶隔離在哪裡執行;哪些測試涵蓋具有多個群組路徑的使用者;為什麼屬於第二個附加群組的成員會失敗。提出最小且安全的變更以及驗證所需的測試。暫時不要編輯檔案。」

審查提案後,我會授權實作並要求一份包含實際指令和結果的報告。我還會要求代理識別遺漏的邊緣案例。這使得代理成為一個有用的實作夥伴,但不會使其成為正確性的最終權威。

CLAUDE.md 中的指令可以要求代理在其回應中包含測試證據。這是一個有用的工作慣例,而不是強制機制。格式良好的報告的存在並不能證明底層證據是有效的。

如果變更方向錯誤,Claude Code 的 /rewind 可以將對話和程式碼恢復到檢查點。這有助於管理實作過程,但不能取代版本控制或驗收測試。

當每個檢查都有特定的職責時,驗證效果會更好。編譯、自動化測試、瀏覽器互動和人工審查觀察系統的不同屬性。執行更多檢查不一定更好;選擇涵蓋不同故障模式的檢查才是關鍵。

靜態檢查:變更是否結構有效? 我從儲存庫中已定義的檢查開始。對於 TypeScript 專案,這些通常包括型別檢查、程式碼風格檢查(linting)和建構驗證。它們揭示了損壞的匯入、不相容的型別、無效的程式碼模式和一些整合錯誤。

例如:pnpm -F api exec tsc --noEmit。這個指令只有在工作區和 TypeScript 配置支援時才有意義。成功的結果告訴我相關的 TypeScript 檢查已通過,但它並不能證明群組成員資格已正確評估,或 API 返回了預期的授權決策。

我會記錄執行了哪個指令、其退出狀態,以及檢查是否涵蓋了變更的程式碼。「靜態檢查通過」不如確切的指令和結果那麼有資訊量。

重點測試:行為是否符合合約? 考慮這個不正確的權限檢查:const group = await db.resourceGroup.findFirst({ where: { resourceId, }, }); const allowed = group ? await isGroupMember(userId, group.groupId) : false;

這個實作只針對資料庫返回的第一個群組檢查成員資格。如果一個資源屬於群組 A 和 B,而使用者只屬於 B,則可能會被拒絕存取。程式碼會編譯通過,並且使用 A 群組成員的測試可能會通過。

修正應該是詢問是否存在至少一個符合條件的成員資格,而不是使用者是否屬於任意選取的一個群組。一種可能的方法是使用關聯式查詢,將租戶條件保留在授權判斷式中:

```typescript

const allowed = await db.resourceGroup.count({

where: {

resourceId,

resource: { tenantId, },

group: {

tenantId,

memberships: { some: { userId, }, },

},

},

}) > 0;

```

確切的查詢取決於應用程式的資料模型和 ORM 綱要。其基本特性是它評估所有符合條件的關係,同時保留租戶邊界。周圍的授權流程仍必須驗證使用者的租戶上下文和任何額外的存取規則。

回歸測試比通用的「授權使用者成功」測試更有價值:

```typescript

it("allows a member of the second attached group", async () => {

const resource = await createResource({ tenantId: tenantA.id, groupIds: [groupA.id, groupB.id], });

const user = await createUser({ tenantId: tenantA.id, groupIds: [groupB.id], });

const response = await requestResource({ resourceId: resource.id, asUser: user, });

expect(response.status).toBe(200);

});

```

這裡的 createResource、createUser 和 requestResource 代表應用程式特定的測試輔助函數。實際的實作應該使用儲存庫現有的夾具(fixtures)和請求工具,並進行確定性設定,明確相關的群組排序。

回歸證明:測試是否檢測到原始錯誤? 通過的測試很有用。一個在錯誤實作下失敗,而在修正後通過的測試,提供了更強的證據,證明它檢測到了預期的缺陷。

在受控的工作樹中執行此檢查的一種方法是:git stash push src/auth/permissions.ts; npm test -- permissions; git stash pop。

這個序列假設工作樹狀態、暫存內容和測試指令已事先檢查。暫存必須確實恢復已知的錯誤實作,測試才有意義。它也可能暫存命名檔案中的其他未提交編輯,所以我會使用一個乾淨、隔離的工作樹,而不是隨意地對活躍工作執行它。

請注意另一個操作細節:使用分號 ; 組合測試和恢復操作,即使測試失敗,恢復嘗試也會執行。如果使用三個單獨的指令,我必須在預期失敗後明確執行恢復,並檢查最終的工作樹狀態。

我想要的證據是具體的:回歸測試在沒有修正的情況下因預期的授權原因而失敗,然後在恢復修正後通過。由於缺少夾具、無效匯入或環境設定導致的失敗,並不能證明回歸測試檢測到了原始錯誤。

儲存庫可能使用 pnpm 而不是 npm:pnpm -F api test -- permissions。我會使用與實際專案匹配的指令,而不是僅僅為了匹配範例而更改套件管理器。

瀏覽器檢查:使用者是否看到了正確的結果? API 測試無法證明瀏覽器正確處理了狀態。一個常見的案例是自動化將文字輸入到使用 DraftJS 構建的富文本編輯器中。

Share

覺得值得一看?分享給朋友。

以上為 AI 自動翻譯導讀。原文版權歸 DEV Community (Claude Code) 所有, 建議透過「閱讀原文」前往原始網站,以取得最完整資訊並支持原作者。

Newsletter

不用每天追,AI 重點每週寄給你

43 個來源、每 2 小時更新,太多看不完。電子報每週挑最值得看的幾則,附上 Max 的看法。

每週一封。Email 只用來寄電子報,每封信都能一鍵退訂。