:測試、審查與重構(gòu))
先從一個問題開始如果你已經(jīng)用過 Cursor、Copilot、通義靈碼、Codex 這類 AI 編程工具大概率會有同樣的感受——代碼生成速度確實快但“生成得快”和“質(zhì)量能用”是兩回事。AI 能在幾秒內(nèi)鋪出幾百行代碼也能在幾秒內(nèi)給你塞進(jìn)一個沒見過的 API、一個越權(quán)漏洞、一段沒人看得懂的嵌套邏輯。Uncle BobRobert C. Martin近期圍繞“AI 時代軟件工程基礎(chǔ)”做過專題討論態(tài)度很直接AI 不會取消軟件工程反而會把軟件工程的基礎(chǔ)能力變成更稀缺、更值錢的東西。這篇文章不聊“AI 會不會取代程序員”這類情緒化話題而是把 Uncle Bob 在這次討論中強(qiáng)調(diào)的觀點拆開落到我們?nèi)粘懘a、審代碼、跑測試的真實場景里。你會看到為什么 AI 時代反而要重新重視測試、設(shè)計、重構(gòu)和代碼整潔怎么用“先測試、后實現(xiàn)”的流程約束 AI 生成的代碼以及團(tuán)隊在引入 AI 編程工具時應(yīng)該補(bǔ)哪些工程治理動作。如果你正在用 AI 輔助寫業(yè)務(wù)代碼、做項目腳手架或者你是帶團(tuán)隊的技術(shù)負(fù)責(zé)人這篇文章建議直接收藏。文章沒有平臺綁定、沒有私有配置所有思路都可以在現(xiàn)有開發(fā)流程里逐步落地。1. Uncle Bob 是誰以及這場討論的視角Uncle Bob 是 Robert C. Martin 的圈內(nèi)稱呼。他是《代碼整潔之道》Clean Code、《架構(gòu)整潔之道》Clean Architecture的作者也是 SOLID 設(shè)計原則的提出者軟件工藝運動的代表人物之一。過去幾十年他一直在強(qiáng)調(diào)專業(yè)主義、測試驅(qū)動開發(fā)TDD、小步提交、持續(xù)重構(gòu)這些基礎(chǔ)動作。這次討論的標(biāo)題已經(jīng)點明了矛盾點AI 時代大家都在追新框架、新模型、新提示詞技巧Uncle Bob 反而把話題拉回“軟件工程基礎(chǔ)”。他的視角不是反對 AI 編程而是認(rèn)為 AI 工具解決的是“代碼產(chǎn)出速度”沒有解決“軟件能否長期維護(hù)、是否可靠、是否可驗證”的問題。更準(zhǔn)確地說AI 讓“寫代碼”這個動作變便宜了但軟件工程里真正昂貴的東西沒有變確認(rèn)需求是否正確驗證代碼行為是否符合預(yù)期處理邊界條件和異??刂萍夹g(shù)債務(wù)的增長讓代碼在幾個月后仍能被團(tuán)隊理解。這些恰恰是軟件工程基礎(chǔ)要解決的問題。AI 生成代碼量越大這些問題就越突出。如果基礎(chǔ)不牢AI 只是把問題從“寫代碼慢”轉(zhuǎn)移成“審查量大、返工多、線上故障爆炸”。2. 核心觀點速覽AI 時代仍需軟件工程基礎(chǔ)結(jié)合這場的討論方向Uncle Bob 關(guān)于“AI 時代軟件工程基礎(chǔ)”的核心觀點可以整理成一張速覽表觀點維度核心主張落到日常開發(fā)的含義專業(yè)主義開發(fā)者必須對自己交付的代碼負(fù)責(zé)不能拿“AI 生成的”當(dāng)免責(zé)理由合入前必須審查、測試、驗證測試紀(jì)律自動化測試是驗證代碼行為的唯一可靠手段沒有測試的 AI 代碼視為未完成清晰設(shè)計代碼要容易被人類閱讀和修改而不是只追求能運行提示詞生成的長函數(shù)、大函數(shù)必須拆分重構(gòu)小步迭代小步提交、快速反饋、頻繁集成不要讓 AI 一次性生成整個模塊擁抱變化AI 是工具工程原則不會失效把 AI 當(dāng)作結(jié)對程序員而不是免檢代碼源這張表其實把 Uncle Bob 過去幾十年的主張原封不動地搬到了 AI 場景。它并不新鮮但在 AI 生成代碼大量進(jìn)入倉庫的今天這些“老規(guī)則”反而成了唯一的防線。這里有個問題值得認(rèn)真想以前我們寫代碼每次敲鍵盤都受到物理速度限制所以代碼量是可控的?,F(xiàn)在 AI 幾分鐘就能生成幾千行代碼倉庫的膨脹速度遠(yuǎn)超人類維護(hù)能力。如果團(tuán)隊沒有測試基線、沒有審查流程、沒有重構(gòu)習(xí)慣AI 帶來的不是效率而是債務(wù)。3. AI 帶來的四個真實變化AI 編程工具普及后軟件開發(fā)過程實際上發(fā)生了四個變化。理解這些變化才能明白為什么“基礎(chǔ)”比“技巧”更重要。3.1 需求澄清成為瓶頸以前寫代碼需求不清晰時程序員會因為寫代碼成本高而反復(fù)追問?,F(xiàn)在 AI 寫代碼幾乎零成本很多開發(fā)者直接拿模糊需求去生成代碼出來的結(jié)果自然偏得離譜。Uncle Bob 一直強(qiáng)調(diào)“軟件開發(fā)的真正困難是確定什么是想要的以及確認(rèn)做出來了什么”。AI 時代提示詞本身就是需求描述。你寫不清提示詞AI 當(dāng)然寫不清代碼。這意味著需求分析、任務(wù)拆分、驗收標(biāo)準(zhǔn)定義這些基礎(chǔ)能力變成了“提示詞工程”的上游。3.2 代碼審閱成為最高頻動作AI 生成代碼之后團(tuán)隊的主要工作從“寫”變成“讀和判斷”。判斷代碼是否滿足需求、是否引入安全風(fēng)險、是否破壞了既有設(shè)計、是否埋了隱藏狀態(tài)。審閱能力以前是資深開發(fā)者的加分項現(xiàn)在是所有使用 AI 工具的開發(fā)者的必備項。如果一個團(tuán)隊習(xí)慣了“AI 生成 - 直接合入”那就等于把質(zhì)量決定權(quán)交給了模型這是高風(fēng)險操作。3.3 依賴與供應(yīng)鏈風(fēng)險放大AI 模型很容易根據(jù)訓(xùn)練數(shù)據(jù)中的模式推薦第三方依賴甚至生成不存在的包名。如果團(tuán)隊不做依賴審查輕則編譯失敗重則引入惡意包。AI 生成代碼越猛依賴注入的面就越寬供應(yīng)鏈風(fēng)險也隨之變大。3.4 技術(shù)債務(wù)加速累積AI 生成代碼時沒有“歷史包袱”的概念。它不會因為你現(xiàn)有代碼結(jié)構(gòu)去主動適配只會針對當(dāng)前提示詞生成一個局部最優(yōu)解。多個局部最優(yōu)解拼在一起往往就是全局的大混亂。所以AI 時代的技術(shù)債務(wù)不是在減少而是在加速。清理債務(wù)的能力——重構(gòu)、梳理依賴、統(tǒng)一設(shè)計風(fēng)格——決定了團(tuán)隊能不能消化 AI 帶來的增量。4. 基本功沒有消失只是載體變了很多人誤以為“AI 會寫代碼了所以我不用學(xué)設(shè)計模式、不用寫測試、不用做重構(gòu)了”。這個判斷恰恰反了。拿測試來說。以前寫測試是為了防止自己改壞代碼?,F(xiàn)在寫測試除了回歸保障還有一個新作用作為 AI 生成結(jié)果的驗收器。你給 AI 一個需求描述它給你一堆代碼怎么判斷它寫對了最靠譜的方式就是跑測試。測試不只是質(zhì)量保障它已經(jīng)變成你和 AI 之間的“合同”。拿設(shè)計原則來說。以前自己寫代碼會不由自主地考慮模塊邊界。現(xiàn)在 AI 生成代碼是面向提示詞的它不會想“這個函數(shù)放這個類里合不合適”。如果開發(fā)者不理解單一職責(zé)、不理解依賴方向AI 生成的代碼會迅速腐化成一個巨型類網(wǎng)絡(luò)。再拿代碼整潔度來說。AI 生成的分支判斷、異常處理經(jīng)常是疊加式增長一個函數(shù)幾十行 if-else 嵌套是常態(tài)。整潔代碼的能力就是把這些內(nèi)容拆回人類能理解的樣子。它沒有過時而是變成了 AI 代碼合入前的必修課。所以結(jié)論是軟件工程基礎(chǔ)沒有消失只是從“編代碼的手藝”變成了“判斷、約束、修正 AI 結(jié)果的能力”。工具越強(qiáng)人的判斷力越貴。5. 落地把“先測試”變成 AI 輔助開發(fā)的主流程Uncle Bob 是 TDD 的堅定倡導(dǎo)者。在 AI 時代TDD 的價值反而更好理解TDD 天然就是一個把“需求”轉(zhuǎn)化為“可執(zhí)行驗收條件”的流程。傳統(tǒng) TDD 流程是寫測試 - 看它失敗 - 寫實現(xiàn) - 測試通過 - 重構(gòu)。AI 輔助開發(fā)時只需要把“寫實現(xiàn)”這個動作交給 AI剩下的過程完全不變明確一個小的行為目標(biāo)比如“結(jié)算時普通用戶滿 100 減 20會員一律 9 折不疊加滿減”。先用測試框架把這個行為描述成測試用例。把測試用例丟給 AI讓它生成能通過測試的最小實現(xiàn)。運行測試根據(jù)失敗信息要求 AI 修正。測試通過后人工閱讀代碼檢查設(shè)計和邊界。合入前重構(gòu)把 AI 生成代碼整理成可維護(hù)的結(jié)構(gòu)。這個流程的價值在于AI 的“想象力”被測試用例死死框住。它不需要理解業(yè)務(wù)全貌只需要滿足測試描述的行為。而開發(fā)者則保留了最重要的驗收權(quán)和判斷權(quán)。6. 用測試用例框住 AI 生成的代碼下面用一個訂單金額計算的例子演示這個流程。假設(shè)業(yè)務(wù)規(guī)則是普通用戶滿 100 減 20會員一律 9 折不與滿減疊加金額不能為負(fù)數(shù)。先寫測試文件# tests/test_order.py import pytest from order_service import calc_total def test_normal_user_below_threshold(): assert calc_total(80, is_memberFalse) 80 def test_normal_user_full_reduction(): assert calc_total(120, is_memberFalse) 100 def test_member_discount_no_full_reduction(): assert calc_total(100, is_memberTrue) 90 def test_member_high_amount_no_stack(): assert calc_total(300, is_memberTrue) 270 def test_negative_amount_raises(): with pytest.raises(ValueError): calc_total(-1, is_memberFalse)把這組測試作為提示詞材料發(fā)給 AI要求它生成實現(xiàn)。AI 給出的可能如下# order_service.py def calc_total(amount: float, is_member: bool False) - float: if amount 0: raise ValueError(amount must be 0) if is_member: return round(amount * 0.9, 2) if amount 100: amount - 20 return round(amount, 2)運行測試pytest tests/test_order.py -v預(yù)期結(jié)果5 passed in 0.02s這樣AI 生成代碼是否合格不是由感覺決定而是由測試結(jié)果決定。如果 AI 第一次沒寫對我們可以把失敗信息貼回去讓它繼續(xù)改。整個迭代過程可控、可回溯。這個例子雖然簡單但它展示了 AI 輔助開發(fā)的正確姿勢先有驗收標(biāo)準(zhǔn)再讓 AI 生產(chǎn)代碼。業(yè)務(wù)規(guī)則再復(fù)雜只要能被拆成可驗證的行為都可以用同樣的方式約束。7. AI 生成代碼的人工審查清單測試通過不代表可以直接合入。AI 代碼經(jīng)常有測試覆蓋不到的問題所以人工審查不能省。下面是一份 AI 生成代碼審查清單可以直接復(fù)制到團(tuán)隊代碼評審流程里。審查項關(guān)注點風(fēng)險等級邊界條件負(fù)數(shù)、空值、超大值、零、空字符串是否處理高異常處理異常類型是否準(zhǔn)確會不會吞掉真實錯誤高安全性SQL 拼接、命令執(zhí)行、文件路徑、越權(quán)訪問高依賴來源是否引入不存在的包、版本是否鎖定、許可證是否可商用高隱式副作用函數(shù)是否只做聲明的事會不會修改外部狀態(tài)中并發(fā)與狀態(tài)全局變量、共享內(nèi)存、競態(tài)條件中性能是否存在明顯 O(n^2)、N1 查詢、循環(huán)內(nèi)調(diào)用慢操作中可讀性函數(shù)長度、命名清晰度、分支嵌套層次中測試覆蓋是否為新增邏輯補(bǔ)充了對應(yīng)測試高與既有架構(gòu)一致性是否沿用團(tuán)隊既有模式還是另起一套風(fēng)格中注意這份清單里風(fēng)險等級為“高”的項必須逐條人工確認(rèn)不能依賴 AI 自查。AI 生成代碼的“自信感”很強(qiáng)即使錯了它也會給出完整解釋所以審查者需要帶著懷疑去看而不是帶著確認(rèn)去看。8. 團(tuán)隊落地建議規(guī)范、基線、培訓(xùn)與合規(guī)如果你不是個人開發(fā)者而是帶團(tuán)隊引入 AI 編程工具以下四個動作值得優(yōu)先做。8.1 建立 AI 代碼合入規(guī)范團(tuán)隊要明確AI 生成的代碼不是免檢代碼。到達(dá)合入門禁前必須滿足和人類代碼相同的檢查要求包括測試通過、評審?fù)ㄟ^、靜態(tài)檢查通過??梢栽?CI 里增加一條規(guī)則不附帶測試的 AI 生成代碼不允許合入主分支。8.2 守住測試基線沒有測試基線的團(tuán)隊先不要大規(guī)模引入 AI 生成代碼。因為 AI 代碼一旦進(jìn)入一個沒有保護(hù)網(wǎng)的倉庫任何一次“看起來對但實際錯”的生成結(jié)果都可能變成線上事故。測試覆蓋率不求一步到位但核心業(yè)務(wù)鏈路必須有自動化測試。8.3 培訓(xùn)內(nèi)容要增加“審查”和“重構(gòu)”團(tuán)隊引入 AI 工具后培訓(xùn)不應(yīng)該只教“怎么寫提示詞”更要教“怎么審查 AI 生成的代碼”和“怎么重構(gòu) AI 生成的大函數(shù)”。從實際經(jīng)驗看提示詞能力提升帶來的收益很快會觸及天花板而審查與重構(gòu)能力決定團(tuán)隊能消化多少 AI 代碼量。8.4 注意合規(guī)與授權(quán)使用 AI 編程工具時需要關(guān)注幾點訓(xùn)練數(shù)據(jù)是否包含受版權(quán)保護(hù)的代碼、生成代碼的許可證是否合規(guī)、公司代碼是否被發(fā)送到第三方接口、生成結(jié)果能否用于商業(yè)項目。這些問題沒有統(tǒng)一答案取決于你使用的工具和部署方式。穩(wěn)妥的做法是敏感項目用私有化部署模型外部工具只處理非敏感任務(wù)合入前檢查依賴許可證。9. 常見誤區(qū)與排查AI 輔助開發(fā)在落地過程中有幾個高頻誤區(qū)單獨列出來提醒。誤區(qū)表現(xiàn)實際情況建議做法讓 AI 一次生成整個模塊代碼量越大錯誤越難定位審查成本越高拆成小任務(wù)逐個驗證不加測試就讓 AI 寫實現(xiàn)沒有驗收標(biāo)準(zhǔn)AI 經(jīng)?!熬幍煤芟竦珜Σ簧闲枨蟆毕葘憸y試再讓 AI 實現(xiàn)測試通過就合入測試只能證明部分行為正確覆蓋不到設(shè)計和安全問題測試之后加上人工審查發(fā)現(xiàn) AI 生成了不存在的依賴模型根據(jù)訓(xùn)練模式推測包名可能寫錯手動確認(rèn)依賴存在且版本正確AI 反復(fù)修改仍不通過測試提示詞里缺少約束或需求本身有歧義回到需求澄清更新測試用例生成代碼風(fēng)格和項目不一致模型不了解項目既有風(fēng)格在提示詞中給出項目風(fēng)格規(guī)范或合入前統(tǒng)一格式化排查 AI 生成代碼問題時最有效的思路不是“繼續(xù)追問 AI”而是“回到測試用例”。測試通過但行為不對說明測試寫錯了測試失敗但實現(xiàn)看起來合理說明需求描述和實現(xiàn)理解不一致。兩種情況都需要人工介入而不是讓模型再猜一輪。10. 總結(jié)先做三件事Uncle Bob 這次討論最值得帶走的不是某個具體技巧而是一個判斷AI 降低的是“寫代碼”的成本不是“做軟件”的成本。軟件工程里的需求、測試、設(shè)計、重構(gòu)、審查每一項都不會因為 AI 消失反而會因為代碼生成量暴增而變得更加關(guān)鍵。如果你想知道從哪里開始建議先做三件事。第一為你最常用的一條業(yè)務(wù)鏈路補(bǔ)一套自動化測試。這是你判斷 AI 生成代碼是否正確的最低成本工具。第二把“AI 生成 - 直接合入”改成“AI 生成 - 測試驗證 - 人工審查 - 合入”。哪怕流程慢一點也比上線后返工強(qiáng)。第三每周挑一段 AI 生成的代碼做一次重構(gòu)練習(xí)。拆長函數(shù)、改命名、清理嵌套分支練的是你在 AI 時代最需要的判斷力。舊工程基礎(chǔ)在 AI 時代沒有過時它只是換了一種方式在篩選開發(fā)者真正理解需求、掌握測試、懂得設(shè)計、愿意對代碼負(fù)責(zé)的人會借助 AI 走得更遠(yuǎn)把這套基礎(chǔ)扔掉的人只會被 AI 生成的代碼量淹沒。建議收藏備用。下次讓 AI 幫你寫代碼之前先問一句這堆代碼的測試在哪