一次合并請(qǐng)求:一個(gè)批量歸檔功能的交付閉環(huán))
原文鏈接AI 輔助編程實(shí)戰(zhàn)從需求邊界到可審查交付開發(fā)中最容易出現(xiàn)的誤解是只要把需求丟給 AI它就能交付一個(gè)功能。實(shí)際上AI 擅長整理上下文、生成候選方案、補(bǔ)全局部實(shí)現(xiàn)、解釋報(bào)錯(cuò)和發(fā)現(xiàn)可疑點(diǎn)需求邊界、技術(shù)取舍、風(fēng)險(xiǎn)判斷、測(cè)試驗(yàn)證與最終合并責(zé)任仍然必須由人承擔(dān)。下面用一個(gè)基于常見內(nèi)部系統(tǒng)改編的案例跑完整個(gè)閉環(huán)。它不是某個(gè)具體公司的真實(shí)項(xiàng)目復(fù)盤但保留了團(tuán)隊(duì)開發(fā)中常見的權(quán)限、歷史數(shù)據(jù)、接口兼容、測(cè)試與代碼審查約束。案例給任務(wù)系統(tǒng)增加“批量歸檔”團(tuán)隊(duì)已有一個(gè)任務(wù)系統(tǒng)。任務(wù)完成后不會(huì)立即刪除而是保留在“已完成”列表中。運(yùn)營同學(xué)提出新需求當(dāng)前項(xiàng)目的項(xiàng)目管理員可以一次選擇多條已完成任務(wù)并歸檔歸檔后默認(rèn)任務(wù)列表不再展示但具備該項(xiàng)目歸檔查看權(quán)限的管理員可在歸檔列表中恢復(fù)。這句話還不能直接開始寫代碼因?yàn)樗鼪]有明確權(quán)限范圍、狀態(tài)轉(zhuǎn)換、重復(fù)請(qǐng)求和返回結(jié)果。先把需求翻譯成可交付的邊界可以讓 AI 先做“需求質(zhì)檢員”復(fù)述已知條件列出歧義和潛在風(fēng)險(xiǎn)但最終規(guī)則必須由產(chǎn)品、開發(fā)或負(fù)責(zé)人確認(rèn)。本例最終確認(rèn)如下項(xiàng)目確認(rèn)結(jié)果操作對(duì)象僅允許歸檔狀態(tài)為completed且尚未歸檔的任務(wù)操作權(quán)限只有當(dāng)前項(xiàng)目的項(xiàng)目管理員可操作成員只能查看操作范圍單次最多歸檔 100 條且任務(wù)必須屬于當(dāng)前項(xiàng)目數(shù)據(jù)策略不物理刪除寫入archivedAt、archivedBy冪等性已歸檔任務(wù)再次提交時(shí)不報(bào)錯(cuò)、不重復(fù)寫入并返回可識(shí)別原因返回結(jié)果返回成功數(shù)、跳過數(shù)以及可安全披露的跳過原因非目標(biāo)本期不做自動(dòng)歸檔、跨項(xiàng)目批量操作和歸檔導(dǎo)出可驗(yàn)收標(biāo)準(zhǔn)是管理員能歸檔本項(xiàng)目中符合條件的任務(wù)無權(quán)限用戶不能產(chǎn)生寫入未完成、已刪除、跨項(xiàng)目或不存在的任務(wù)不會(huì)被錯(cuò)誤歸檔重復(fù)點(diǎn)擊不會(huì)產(chǎn)生重復(fù)副作用歸檔后默認(rèn)列表、歸檔列表、分頁和篩選結(jié)果符合既有規(guī)則。**人機(jī)分工很明確**AI 可以幫助暴露問題人必須決定規(guī)則。規(guī)則沒有定下來再漂亮的代碼也只是把猜測(cè)固化進(jìn)系統(tǒng)。第一步先拆“小變更”不要索要“一整個(gè)功能”本例可以拆成六個(gè)任務(wù)確認(rèn)數(shù)據(jù)模型歸檔字段、恢復(fù)字段和必要索引。定義接口契約請(qǐng)求參數(shù)、響應(yīng)、錯(cuò)誤和冪等行為。實(shí)現(xiàn)權(quán)限、項(xiàng)目歸屬和狀態(tài)校驗(yàn)。實(shí)現(xiàn)批量更新并記錄操作者與時(shí)間。調(diào)整默認(rèn)列表和歸檔列表查詢。補(bǔ)齊測(cè)試、變更說明、風(fēng)險(xiǎn)點(diǎn)和驗(yàn)證結(jié)果??梢砸?AI 每次只處理一個(gè)任務(wù)并固定返回修改哪些文件、依賴哪些假設(shè)、尚未覆蓋哪些場(chǎng)景、應(yīng)執(zhí)行哪些驗(yàn)證命令。這樣得到的是一系列可追溯的候選變更而不是一大段難以審查的代碼。第二步讓 AI 比較方案而不是替你做架構(gòu)決定批量歸檔至少有兩種實(shí)現(xiàn)方式。方案 A逐條讀取并更新讀取任務(wù)、逐條校驗(yàn)、逐條更新并匯總結(jié)果。優(yōu)點(diǎn)是失敗原因細(xì)缺點(diǎn)是數(shù)據(jù)庫往返次數(shù)多并且要明確部分成功時(shí)的事務(wù)語義。方案 B按條件批量更新再計(jì)算結(jié)果先驗(yàn)證當(dāng)前用戶是否為該項(xiàng)目管理員再用“項(xiàng)目 ID 任務(wù) ID 集合 已完成 未歸檔”的條件批量更新隨后根據(jù)影響行數(shù)和補(bǔ)充查詢計(jì)算成功、跳過結(jié)果。優(yōu)點(diǎn)是通常更高效缺點(diǎn)是要額外設(shè)計(jì)逐條原因的查詢和并發(fā)語義。本例選擇方案 B因?yàn)閱未巫疃?100 條且只要求匯總級(jí)結(jié)果。需要注意僅憑updateMany的影響行數(shù)通常不能區(qū)分“不存在”“跨項(xiàng)目”“未完成”和“已歸檔”如果接口承諾逐條原因就必須補(bǔ)充受控查詢或調(diào)整接口契約。AI 可以比較復(fù)雜度、失敗模式和索引需求但事務(wù)邊界、ORM 能力、并發(fā)規(guī)則以及是否值得提前抽象必須由開發(fā)者結(jié)合現(xiàn)有代碼庫決定。為了一個(gè)歸檔功能新增通用規(guī)則引擎、事件總線或多層策略類可能只是過度設(shè)計(jì)。Google 的代碼審查實(shí)踐也強(qiáng)調(diào)應(yīng)優(yōu)先解決已經(jīng)明確存在的問題而不是為想象中的未來需求堆疊復(fù)雜度。第三步把接口寫成可驗(yàn)證的契約POST /projects/:projectId/tasks/archive Content-Type: application/json { taskIds: [task_101, task_102, task_103] }成功響應(yīng)示例{ archivedCount: 2, skipped: [ { taskId: task_103, reason: ALREADY_ARCHIVED } ] }還要提前規(guī)定空數(shù)組、重復(fù) ID、超過 100 條、無權(quán)限、跨項(xiàng)目、未完成、已刪除或不存在、數(shù)據(jù)庫失敗以及請(qǐng)求期間發(fā)生并發(fā)恢復(fù)或狀態(tài)修改時(shí)的行為。常見錯(cuò)誤是只按 ID 更新遺漏projectId條件。權(quán)限、歸屬和狀態(tài)應(yīng)當(dāng)同時(shí)出現(xiàn)在服務(wù)端的查詢或更新條件中而不能依賴前端傳參或 AI 的口頭保證。偽代碼可以接近c(diǎn)onst result await taskRepository.updateMany({ where: { id: { in: uniqueTaskIds }, projectId, status: completed, archivedAt: null }, data: { archivedAt: now, archivedBy: currentUser.id } });這段代碼仍不完整它沒有展示管理員身份驗(yàn)證、參數(shù)校驗(yàn)、錯(cuò)誤映射、審計(jì)日志、逐條跳過原因和事務(wù)邊界。第四步讓 AI 寫局部代碼同時(shí)暴露假設(shè)較好的順序是先解釋倉庫中相似功能再草擬服務(wù)方法簽名人工確認(rèn)接口和錯(cuò)誤碼后實(shí)現(xiàn)最小更新邏輯最后生成測(cè)試骨架和審查建議。每一步都要求 AI 標(biāo)出假設(shè)例如假設(shè)TaskRepository已有支持條件更新的updateMany方法權(quán)限中間件已保證登錄態(tài)未覆蓋并發(fā)恢復(fù)任務(wù)與歸檔請(qǐng)求同時(shí)發(fā)生的情形。假設(shè)清單能把隱蔽的模型幻覺變成可檢查項(xiàng)目。GitHub 對(duì) AI 編程助手的說明也提醒模型建議不一定最優(yōu)或完整使用者仍需驗(yàn)證輸出。第五步測(cè)試不是讓 AI“補(bǔ)幾個(gè)用例”測(cè)試應(yīng)從驗(yàn)收標(biāo)準(zhǔn)反推??梢韵冉⒕仃囋僮?AI 補(bǔ)充測(cè)試骨架、測(cè)試數(shù)據(jù)和反例測(cè)試維度關(guān)鍵場(chǎng)景預(yù)期結(jié)果正常路徑管理員歸檔 3 個(gè)已完成任務(wù)3 條被歸檔默認(rèn)列表不再出現(xiàn)權(quán)限普通成員發(fā)起歸檔無權(quán)限且不產(chǎn)生寫入歸屬提交其他項(xiàng)目任務(wù) ID不更新并按契約返回跳過或拒絕狀態(tài)提交進(jìn)行中的任務(wù)不歸檔并返回原因冪等已歸檔任務(wù)再次提交不重復(fù)寫入結(jié)果可解釋輸入校驗(yàn)空數(shù)組、重復(fù) ID、超過 100 條返回參數(shù)錯(cuò)誤并發(fā)歸檔期間任務(wù)被恢復(fù)符合既定事務(wù)或條件更新約定查詢回歸默認(rèn)列表、歸檔列表、分頁篩選舊功能不受影響失敗處理數(shù)據(jù)庫異常或超時(shí)返回統(tǒng)一錯(cuò)誤不泄露內(nèi)部細(xì)節(jié)還可以讓 AI 扮演“反例生成器”如果刪除projectId條件哪條測(cè)試會(huì)失敗如果漏掉archivedAt: null重復(fù)請(qǐng)求會(huì)怎樣如果權(quán)限判斷放在批量更新之后數(shù)據(jù)會(huì)發(fā)生什么好的測(cè)試不只是“跑綠”還要確認(rèn)錯(cuò)誤實(shí)現(xiàn)確實(shí)會(huì)讓測(cè)試失敗。Google 的審查指南建議在同一變更中檢查生產(chǎn)代碼及相應(yīng)測(cè)試并確認(rèn)斷言有效、不會(huì)產(chǎn)生假陽性。第六步用失敗的生成結(jié)果練習(xí)審查async archiveTasks(taskIds: string[]) { return this.taskRepository.updateMany({ where: { id: { in: taskIds } }, data: { archivedAt: new Date() } }); }這段代碼可能通過最簡(jiǎn)單的成功測(cè)試但不能直接合并至少存在以下問題沒有權(quán)限校驗(yàn)、項(xiàng)目歸屬限制、狀態(tài)限制、冪等約束、操作者審計(jì)字段和可解釋結(jié)果。它說明了一個(gè)關(guān)鍵現(xiàn)實(shí)局部答案“看起來合理”不等于符合業(yè)務(wù)、工程和安全約束。第七步把 AI 放進(jìn)代碼審查但不要把審批權(quán)交給它提交 Pull Request 后可以讓 AI 總結(jié)改動(dòng)、標(biāo)出可疑空值路徑、檢查重復(fù)邏輯、解釋失敗流水線并提出候選問題但作者和人工評(píng)審者仍需判斷是否修復(fù)、是否符合倉庫約定以及是否可以合并。GitHub 相關(guān)文檔應(yīng)以當(dāng)前產(chǎn)品能力和團(tuán)隊(duì)權(quán)限配置為準(zhǔn)不能把 AI 的評(píng)論當(dāng)成人工批準(zhǔn)。AI 輔助代碼審查清單正確性驗(yàn)收標(biāo)準(zhǔn)、權(quán)限、項(xiàng)目歸屬、狀態(tài)、冪等和并發(fā)是否完整??删S護(hù)性是否沿用既有分層、命名和錯(cuò)誤處理是否引入不必要抽象。安全與隱私是否遺漏后端授權(quán)日志、提示詞和測(cè)試數(shù)據(jù)中是否包含密鑰、令牌、生產(chǎn)數(shù)據(jù)或個(gè)人信息代理是否采用最小權(quán)限高影響操作是否保留人工確認(rèn)。性能與數(shù)據(jù)規(guī)模是否存在 N1 查詢條件字段是否需要索引批量大小和返回體是否合理。異常處理參數(shù)錯(cuò)誤、無權(quán)限、狀態(tài)沖突和系統(tǒng)錯(cuò)誤是否區(qū)分是否泄露內(nèi)部細(xì)節(jié)部分成功能否安全重試。測(cè)試覆蓋是否驗(yàn)證數(shù)據(jù)庫最終狀態(tài)移除權(quán)限或歸屬條件時(shí)測(cè)試是否會(huì)失敗。項(xiàng)目一致性API、錯(cuò)誤碼、日志、審計(jì)、文檔、接口定義和前端調(diào)用是否同步變更是否便于回滾。OWASP 關(guān)于具備代理能力的 LLM 應(yīng)用建議采用最小功能、最小權(quán)限和人工確認(rèn)原則下游系統(tǒng)仍應(yīng)獨(dú)立完成授權(quán)校驗(yàn)不能相信模型自行判斷權(quán)限。一頁式工作流每次讓 AI 參與開發(fā)都留下這些產(chǎn)物階段階段產(chǎn)物AI 可以做什么人必須確認(rèn)什么通過條件需求澄清邊界、非目標(biāo)、驗(yàn)收標(biāo)準(zhǔn)、待確認(rèn)問題復(fù)述需求、發(fā)現(xiàn)歧義業(yè)務(wù)規(guī)則與優(yōu)先級(jí)每條驗(yàn)收標(biāo)準(zhǔn)可驗(yàn)證任務(wù)拆解小任務(wù)清單、依賴關(guān)系、風(fēng)險(xiǎn)排序拆分技術(shù)任務(wù)、列遺漏項(xiàng)粒度與順序每項(xiàng)可獨(dú)立測(cè)試或?qū)彶榉桨冈O(shè)計(jì)接口契約、數(shù)據(jù)變化、方案對(duì)比比較路徑、生成草圖架構(gòu)取舍與兼容性符合現(xiàn)有約定局部編碼小范圍 Diff、假設(shè)清單編寫樣板、重構(gòu)局部邏輯業(yè)務(wù)條件、權(quán)限與異常靜態(tài)檢查和測(cè)試通過測(cè)試調(diào)試測(cè)試矩陣、復(fù)現(xiàn)步驟生成骨架、解釋報(bào)錯(cuò)邊界和修復(fù)有效性錯(cuò)誤可復(fù)現(xiàn)且修復(fù)通過代碼審查PR 描述、風(fēng)險(xiǎn)點(diǎn)、審查記錄總結(jié)改動(dòng)、提出問題是否修復(fù)、是否合并人工審批和流水線通過最后的邊界AI 可以加速但不能替你交付AI 輔助編程的效率不是讓開發(fā)者少思考而是把重復(fù)整理、候選生成、代碼解釋和初步檢查交給工具讓人把時(shí)間集中在理解業(yè)務(wù)、做取舍、驗(yàn)證結(jié)果和承擔(dān)責(zé)任上。真正的交付物不只是一段批量更新代碼而是一組可追溯證據(jù)明確的驗(yàn)收標(biāo)準(zhǔn)、受約束的接口、可解釋的異常處理、覆蓋風(fēng)險(xiǎn)的測(cè)試、可審查的 Pull Request以及人工確認(rèn)后的合并決定??梢詮囊粋€(gè)原則開始每次讓 AI 生成或修改代碼都要求它說明上下文、假設(shè)、未覆蓋場(chǎng)景與驗(yàn)證方式每次準(zhǔn)備合并都由人用測(cè)試和審查把這些回答逐一驗(yàn)真。參考資料GitHub DocsUsing GitHub Copilot to explore pull requestsGitHub DocsUsing GitHub Copilot code reviewGitHub DocsResponsible use of GitHub Copilot ChatGoogle Engineering PracticesWhat to look for in a code reviewNISTSecure Software Development FrameworkOWASPExcessive Agency