范:如何讓AI生成代碼更可靠)
1. 為什么 Flutter 項目最先要解決的是 AI 代碼生成的不確定性先說個我曾經(jīng)真實踩過的坑。某次給一個中等規(guī)模的 Flutter 應(yīng)用加離線緩存模塊項目里已經(jīng)有大半代碼是 AI 輔助生成的我圖省事直接把需求描述丟給 Claude Code讓它自己改數(shù)據(jù)層代碼。結(jié)果它非常利索地改完了所有測試也過了編譯也沒報錯。當時我還挺滿意后來 Code Review 的時候發(fā)現(xiàn)一個問題它把原本項目中統(tǒng)一使用的Either錯誤處理風格改成了try-catch還把狀態(tài)管理里約定好的StateController命名改成了BlocController整個代碼風格直接分裂了。這個問題的本質(zhì)不是哪個 AI 工具不行而是工程規(guī)范缺失。AI 生成代碼和人類寫代碼不一樣人類寫代碼會下意識跟隨項目現(xiàn)有的風格AI 不懂這些項目內(nèi)的潛規(guī)則它只能根據(jù)上下文里的代碼風格去猜測猜不中的時候就會產(chǎn)生這類隱蔽的偏差。更麻煩的是這種偏差在編譯期通??床怀鰜淼却a合入主分支之后才逐步暴露那時候再修成本就高了。后來我搭了一套 Flutter 工程規(guī)范體系核心思路是把項目內(nèi)的代碼風格、架構(gòu)約束、命名約定、文件組織規(guī)則都變成 AI 能讀取、能執(zhí)行的顯式規(guī)則再配合 Claude Code 和 Codex 的自動化檢查能力讓 AI 在生成代碼的第一時間就遵守這些規(guī)則。這套方法跑了一段時間之后AI 生成的代碼質(zhì)量和項目本身代碼的一致性有了明顯提升Code Review 的負擔小了很多。這篇文章就把整套搭建思路完完整整地寫出來包括工具選型、規(guī)則文件怎么寫、Claude Code 和 Codex 怎么配合、以及那些文檔里不會寫的坑。如果你是 Flutter 開發(fā)者并且已經(jīng)在用 AI 輔助寫代碼或者正準備引入 AI 編程工具這篇文章應(yīng)該能幫你省下不少返工的時間。2. 搭規(guī)范前的關(guān)鍵判斷Claude Code 和 Codex 到底各自負責什么很多人在同一項目里同時使用兩個 AI 工具純粹是因為每個都有點優(yōu)勢又不舍得放棄另一個。但如果你不明確它們的分工兩個工具各寫各的反而會讓代碼風格更混亂。我的做法是讓它們分別負責不同的任務(wù)場景并用同一套規(guī)則文件約束輸出。2.1 Claude Code 更適合做重構(gòu)和跨文件修改Claude Code 在處理跨文件的代碼修改、重構(gòu)、架構(gòu)調(diào)整這類任務(wù)時表現(xiàn)更穩(wěn)因為它的上下文窗口更大對項目結(jié)構(gòu)的理解更整體。比如把某個功能模塊從Provider狀態(tài)管理遷移到Riverpod涉及十幾個文件Claude Code 的表現(xiàn)通常比 Codex 更可靠。對于這類任務(wù)我給它配置的規(guī)則重點是保持原有公共接口不變遷移時保留原有測試用例所有新增依賴必須聲明在 pubspec.yaml 中。有了這些約束它做重構(gòu)的時候會更謹慎。2.2 Codex 更適合做小范圍功能生成和代碼補全Codex 的優(yōu)勢在于輕快、便宜適合完成明確的小任務(wù)比如寫一個根據(jù)傳入?yún)?shù)生成分頁列表的 Widget實現(xiàn)一個從 JSON 解析用戶模型的 fromJson 方法這類范圍清晰的代碼生成。它生成代碼速度快在小范圍的獨立文件修改上質(zhì)量很不錯。Codex 的風險在于它不像 Claude Code 那么了解整個項目的上下文所以規(guī)則文件對它更重要。比如它可能不認識項目里自定義的基礎(chǔ)組件AppBaseWidget直接在代碼里新建了一個類似的容器組件這種問題只能靠規(guī)則約束。2.3 兩份獨立規(guī)則文件的配置結(jié)構(gòu)我建議不要只維護一份通用的規(guī)則文件而是為兩個工具分別維護。原因是它們的配置讀取機制不同可用的規(guī)則描述能力也不同。我的配置結(jié)構(gòu)是Claude Code 使用項目根目錄下的CLAUDE.md文件它每次會話開始時會自動讀取Codex 使用項目根目錄下的AGENTS.md文件或者在使用時通過-f參數(shù)指定規(guī)則文件。兩條工具默認讀取的文件不同所以我們分別維護同時把一份共享的工程規(guī)范核心內(nèi)容放在兩個文件里。這樣每個工具體系獨立但規(guī)范核心一致。提示如果你用 Codex 的本地命令行版本每次執(zhí)行時加-f AGENTS.md參數(shù)指定規(guī)則文件避免它因為目錄問題沒讀到規(guī)范。這一點實操中很容易踩坑。2.4 統(tǒng)一規(guī)范核心避免規(guī)則漂移兩個文件雖然分開維護但核心規(guī)范必須一致否則會出現(xiàn) Claude Code 寫的代碼遵守一套規(guī)則、Codex 遵守另一套的情況。我解決這個問題的辦法是把規(guī)范內(nèi)容集中寫在項目根目錄的docs/engineering_guidelines.md文件里CLAUDE.md和AGENTS.md里只寫一句請嚴格遵守docs/engineering_guidelines.md中的全部規(guī)范。這樣只需要維護一份源文檔既避免了多份文件不同步的問題也方便項目里的真人開發(fā)者閱讀同一份規(guī)范。3. 工程規(guī)范文件的核心內(nèi)容讓 AI 能聽懂 Flutter 項目的約束規(guī)范文件要想真正起作用不能只寫代碼風格保持一致這種模糊的話AI 需要的是可執(zhí)行的、具體的規(guī)則。我按照 Flutter 項目的工程特征把規(guī)范拆成了七大塊每塊都有明確的描述和正反示例下面逐一說明。3.1 項目架構(gòu)與目錄結(jié)構(gòu)約束Flutter 項目的架構(gòu)風格直接決定了 AI 生成的文件應(yīng)該放在哪里。如果你的項目用的是 feature-first 架構(gòu)那么 AI 生成的模塊文件就必須放進對應(yīng)功能目錄否則整個目錄會成為大雜燴。我通常會在規(guī)范文件里寫明項目采用 feature-first 目錄結(jié)構(gòu) lib/features/feature_name/ ├── data/ (數(shù)據(jù)源、倉儲實現(xiàn)) ├── domain/ (實體、倉儲接口、用例) └── presentation/ (頁面、組件、狀態(tài)管理) 新增功能代碼必須放入對應(yīng)模塊目錄禁止在 lib/ 根目錄直接創(chuàng)建新文件夾。 共享組件放入 lib/shared/widgets/共享工具類放入 lib/shared/utils/。同時我在規(guī)范里明確寫出禁止事項禁止在 main.dart 中直接寫業(yè)務(wù)邏輯main.dart 只負責初始化與路由配置。這類否定式約束對 AI 特別有效因為 AI 傾向于在描述性的規(guī)則下自行發(fā)揮而否定式規(guī)則能直接阻斷它跑偏。3.2 狀態(tài)管理方案的統(tǒng)一約束Flutter 生態(tài)里的狀態(tài)管理方案非常多項目一旦選定就必須堅持否則 AI 會因為任務(wù)描述里的某個詞匯就切換方案。我見過最典型的場景任務(wù)里提到這個頁面需要一個加載狀態(tài)AI 就直接引入了一套額外的 loading 方案代碼而項目本身已經(jīng)有統(tǒng)一的 loading 框架。我的規(guī)范文件里會明確寫當前的狀態(tài)管理方案并強調(diào)禁止引入新的狀態(tài)管理庫。本項目使用 Riverpod 作為唯一狀態(tài)管理方案。 - 頁面狀態(tài)用 StateProvider 或 NotifierProvider禁止使用 setState 處理跨組件狀態(tài)。 - 異步加載場景統(tǒng)一使用 AsyncValue 處理禁止在 Widget 內(nèi)部手動管理多個 loading 標志位。 - 團隊已封裝 AppAsyncBuilder 組件禁止使用其他方式渲染異步狀態(tài)。配上一個反面示例如果看到語言為setState的代碼必須重寫為 Riverpod 的NotifierProvider并同步調(diào)整測試代碼。正反結(jié)合AI 生成代碼時不會摸不著頭腦。3.3 命名規(guī)范與代碼風格Dart 自帶官方風格指南但項目里通常還會有團隊自己的約定比如私有文件用_前綴、某些特定類型組件用固定后綴等。這些約定要逐條寫上。我自己常用的幾條約束是組件類名使用PascalCase文件名使用snake_case但 StatefulWidget 的 State 類命名為_XxxState頁面級 Widget 統(tǒng)一使用后綴Page業(yè)務(wù)組件統(tǒng)一使用后綴Widget比如LoginPage、UserCardWidget枚舉類型文件名加_enum后綴數(shù)據(jù)模型文件名加_model后綴JSON 解析方法統(tǒng)一命名為fromJson/toJson禁止使用fromMap/toMap常量直接使用abstract final class定義在lib/shared/constants/目錄下禁止在頁面文件里散落魔法數(shù)字。這些規(guī)則看似瑣碎但正是這些細節(jié)最需要 AI 遵守。任何一個不一致的小命名在 Flutter 項目里都會成為 Code Review 的一項糾錯點積累多了會非常影響效率。3.4 依賴注入方式Flutter 項目的 DI依賴注入一般有兩種主流方式使用provider包手動注入或者使用riverpod自帶的容器機制。規(guī)范里明確寫清楚 DI 寫法能避免 AI 生成出無法編譯的代碼。我指定了如下的依賴注入模式禁止在 Widget 構(gòu)造函數(shù)中傳遞 Repository 依賴統(tǒng)一使用 Riverpod 的 Provider 完成依賴注入。 新增的數(shù)據(jù)源、倉儲、用例類必須在對應(yīng)的 provider 文件中完成注冊。 Provider 文件位于 lib/features/feature_name/di/ 目錄。這條規(guī)范特別重要。有一次 Codex 生成一個新的登錄頁面它把所有依賴都通過構(gòu)造函數(shù)傳參傳遞了代碼編譯沒問題但完全破壞了整個項目的依賴注入結(jié)構(gòu)后來我花了半天時間才把它改成 Provider 注入的方式?,F(xiàn)在規(guī)范文件里明確約束之后同類問題基本沒有再出現(xiàn)過。3.5 數(shù)據(jù)層與網(wǎng)絡(luò)層規(guī)范Flutter 項目里最容易出問題的是網(wǎng)絡(luò)請求和數(shù)據(jù)解析部分因為這里涉及的代碼模式多AI 可能生成多種不同的寫法。我要求的統(tǒng)一模式是所有網(wǎng)絡(luò)請求統(tǒng)一使用項目封裝的ApiClient類禁止直接使用dio或http裸調(diào)用接口返回數(shù)據(jù)統(tǒng)一先經(jīng)過ApiResponseT包裝再解析到模型模型必須包含fromJson和copyWith方法所有后端接口的路徑常量定義在lib/shared/constants/api_paths.dart禁止在業(yè)務(wù)代碼中直接寫 URL 字符串?!敖怪苯邮褂?dio 裸調(diào)用”這條我在實踐中發(fā)現(xiàn)必須寫得非常直白。AI 在生成網(wǎng)絡(luò)請求代碼時非常傾向直接把dio.get(...)寫在業(yè)務(wù)代碼里面但它不知道項目里已經(jīng)有封裝好的ApiClient。規(guī)范里明確禁止之后AI 就會主動去找ApiClient并調(diào)用它。3.6 錯誤處理與日志規(guī)范錯誤處理風格混亂是 AI 生成代碼的另一個重災(zāi)區(qū)。有的地方用try-catch有的地方用Either有的地方直接拋異常但不處理。我要求全項目統(tǒng)一使用Result模式業(yè)務(wù)層禁止直接使用 try-catch 捕獲異常統(tǒng)一返回 ResultT sealed class ResultT { ... } 所有有可能失敗的調(diào)用都必須返回 Result 類型由調(diào)用方顯式處理成功與失敗分支。 持久化與框架層代碼可以使用 try-catch但捕獲后必須記錄日志禁止靜默吞掉異常。 日志統(tǒng)一通過 Logger 工具輸出禁止使用 print。這條規(guī)范特別考驗 AI 的理解能力。實際上 Claude Code 在處理這種sealed class 模式時通常表現(xiàn)良好它能理解文檔里對失敗分支處理的描述而 Codex 有時會在非常短的代碼片段里漏掉失敗分支處理所以需要配合下面的自動化檢查來兜底。3.7 測試規(guī)范約束很多 Flutter 開發(fā)者不在意 AI 是否生成測試但如果你想讓 AI 長期參與項目開發(fā)測試規(guī)范必須從一開始就立好。我的要求是新增功能模塊必須配套單元測試測試文件放在 test/ 目錄下并且與模塊目錄結(jié)構(gòu)保持一致。 測試使用 mockito 或 fake 對象禁止依賴真實網(wǎng)絡(luò)請求。 測試命名遵循 given/when/then 風格。AI 生成測試代碼的能力很強特別是 Claude Code它通常能生成覆蓋主干路徑的測試但如果規(guī)范里沒有要求它不會主動寫?,F(xiàn)在這套規(guī)范跑下來AI 生成的功能代碼基本能同步生成測試測試覆蓋率在新增代碼上維持在 85% 以上差不多是真人開發(fā)的水平了。4. 讓規(guī)范真正被執(zhí)行Claude Code 和 Codex 的聯(lián)動工作流規(guī)則文件寫好了但如果只是放在那里AI 未必每次都會完整遵守尤其是復(fù)雜任務(wù)下容易忘掉某些規(guī)則。所以我在規(guī)范文件之外還搭了一層聯(lián)動工作流讓兩個工具互相檢查效果比單獨依賴某一款工具好得多。4.1 開發(fā)流程Claude Code 主寫主審Codex 負責快速生成我運用這套體系有三個月了以下是目前穩(wěn)定運行的流程整個過程分三步需求拆解后先把任務(wù)交給 Claude Code給它完整的上下文包括涉及的功能模塊、現(xiàn)有的關(guān)聯(lián)代碼、必須遵守的規(guī)范條款簡單生成任務(wù)交給 Codex 快速完成。比如寫一個PaginationList組件支持加載更多和刷新功能按規(guī)范命名放在指定目錄Claude Code 生成完主代碼后由 Codex 執(zhí)行一輪獨立的代碼審查檢查它是否違反了規(guī)范文件中的內(nèi)容。為什么要用兩個工具互相審查因為 AI 審查 AI 的代碼和人類審查 AI 的代碼不一樣AI 有一個巨大的優(yōu)勢它極其擅長識別模式偏差。比如命名風格不一致、某個錯誤處理方式偏離規(guī)范這些在 Codex 審查時能被快速定位。4.2 Codex 執(zhí)行規(guī)范審查的 Prompt 模板審查環(huán)節(jié)的 prompt 我攢了一套模板實測效果很好直接貼出來給你參考你是本項目的高級 Flutter 工程師。請嚴格按照 docs/engineering_guidelines.md 中的規(guī)范審查以下文件并只輸出發(fā)現(xiàn)的問題列表 [文件路徑列表] 要求 1. 只報告違反規(guī)范的問題不要修改代碼 2. 對每個問題標明違反的具體規(guī)范條目編號 3. 如果沒有問題輸出通過檢查。這套 prompt 的關(guān)鍵點是只輸出問題列表不要讓它直接修。因為 AI 一旦自己動手改它會傾向于重寫整個文件這往往會引入新的不規(guī)范代碼。讓它只列出問題我們再決定是讓 Claude Code 修改還是手動修改。4.3 人工 Review 的兜底時機你可能會問既然 AI 互審了還需要人工 Review 嗎。我的回答是需要但人的負擔可以大幅減輕。我現(xiàn)在的流程是AI 互審全部通過之后我再自己看一遍 diff重點看這三類內(nèi)容業(yè)務(wù)邏輯是否有遺漏、邊界條件是否處理到位、UI 細節(jié)是否符合設(shè)計稿。而關(guān)于代碼風格是否符合規(guī)范這類問題我基本不再花時間檢查了因為 AI 互審已經(jīng)能比較可靠地兜住這方面問題。這套聯(lián)動流程跑下來我個人的 Code Review 時間減少了至少一半。4.4 小改動怎么處理有人可能會擔心如果只是改一個方法兩三行代碼也走這套流程是不是太重了。我的建議是小改動直接手動改不進 AI 流程凡是新增文件或改動超過三十行都必須走上述聯(lián)動。這套工作流的成本并不高但對于大改動它的價值是實打?qū)嵉摹H绻枰膭佣鄠€文件再給 Claude Code 一次性的完整變更指令讓它一次改完效率更高。5. 自動化檢查層讓規(guī)范違反問題在代碼提交前就被攔下規(guī)則文件和工作流解決的是AI 生成時遵守規(guī)范的問題但 AI 偶爾還是會出現(xiàn)意外。所以我又加了一層自動化檢查確保任何違反規(guī)范的代碼在提交之前就被攔下來。這部分用的是 Dart 生態(tài)里的標準工具加上 CI 配置。5.1 基礎(chǔ)檢查組合analyze format testFlutter 項目基礎(chǔ)的三件套是繞不開的dart analyze靜態(tài)分析能查出大部分編碼問題dart format --set-exit-if-changed檢查代碼格式是否符合規(guī)范flutter test跑測試防止邏輯回歸。這三項我全部掛在 CI 上任何 PR 未通過檢查都不能合并。AI 生成的代碼往往在小細節(jié)上會在這三項上翻車比如工具自動生成的格式和項目格式化配置不一致dart format就能直接體現(xiàn)問題。有一點要注意格式檢查跑在 CI 上沒意義因為開發(fā)機上可以一鍵格式化但 CI 上失敗只會增加一次提交輪次。它可以作為規(guī)范的兜底但不能是唯一的反饋途徑。5.2 自定義架構(gòu)掃描腳本標準檢查之外我還寫了一個自定義的 Dart 腳本tool/check_arch.dart專門掃描項目結(jié)構(gòu)是否符合規(guī)范。這個腳本做的事情很機械但很實用檢查lib/features/下是否有不符合 feature-first 目錄結(jié)構(gòu)的新建目錄檢查是否有業(yè)務(wù)代碼引用dio或http包進行網(wǎng)絡(luò)請求檢查是否有代碼在lib/下直接定義常量而沒有放入lib/shared/constants/檢查是否有頁面類沒有使用Page后綴。這個腳本我沒做得多復(fù)雜幾百行 Dart 代碼用了analyzer包的基礎(chǔ) API遍歷 AST 做字符串和模式匹配。丟到 CI 上跑了這段時間效果出奇地好——它抓到的規(guī)范違反案例比人工 Code Review 更全面因為在代碼量大的情況下人工很難逐行注意到所有細節(jié)。5.3 CI 執(zhí)行鏈的完整配置示例最后放一個我目前正在使用的 CI 配置片段使用常見 CI 平臺的語法格式你可以直接抄過去改改jobs: verify: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 - uses: subosito/flutter-actionv2 with: channel: stable - run: flutter pub get - run: dart analyze - run: dart format --set-exit-if-changed . - run: flutter test --coverage - run: dart tool/check_arch.dart有了這條鏈子每次提交 AI 生成的代碼先過自動檢查再過 AI 互審最后才到人工 Review。檢查的等級是層層遞進的前一層通過之后后面一層的效率就能大幅提升。5.4 自動化檢查遇到的常見坑最后提醒兩個坑。第一個是dart format和編輯器格式化配置不一致的問題我遇到過 CI 上格式檢查失敗但本地 IDE 顯示格式一切正常后來發(fā)現(xiàn)是項目里缺了analysis_options.yaml中對formatter的配置。解決方法是在項目里固定一個dart format配置版本并確保團隊所有人都使用同一版本的 Flutter SDK。第二個坑是自定義掃描腳本容易誤報。比如某些第三方包源碼里本來就有dio依賴腳本掃描的時候會把第三方代碼也算進去。解決辦法是在腳本里增加排除規(guī)則只掃描lib/目錄不掃描第三方包緩存目錄。6. 這套規(guī)范體系的實際效果從參數(shù)到可量化的收益說了這么多最后把效果量化一下畢竟沒有數(shù)據(jù)支撐的方法論很難讓團隊接受。6.1 代碼風格統(tǒng)一度提升配置這套規(guī)范之前AI 生成的代碼和項目既有代碼的風格一致性大概是這樣AI 能看懂文件里的局部風格但跨文件的統(tǒng)一約定基本靠運氣。配置之后我抽了幾次項目的代碼做了統(tǒng)計AI 生成代碼的風格違反率從早期的 15% 左右降到了 3% 以下。這個數(shù)字說明規(guī)范文件本身就有相當強的約束力加上自動化檢查的兜底AI 生成代碼的偏差基本被控制到了可以接受的范圍。6.2 Code Review 效率提升之前每次 Code Review AI 生成的代碼平均要花 45 分鐘到一個小時其中大部分時間是在糾正風格和結(jié)構(gòu)問題真正的業(yè)務(wù)邏輯審查反而沒時間細看?,F(xiàn)在 AI 互審和自動化檢查把結(jié)構(gòu)問題都攔在了前面我的 Review 時間壓縮到 20 分鐘以內(nèi)而且這 20 分鐘可以完全專注在業(yè)務(wù)邏輯上。團隊里的 A 同學(xué)之前對 AI 生成代碼很不信任覺得每次都要重新改一遍。這套流程跑了一個多月之后他主動說現(xiàn)在 AI 提交的代碼質(zhì)量有時候比自己手寫的還穩(wěn)定至少命名和目錄結(jié)構(gòu)從來不用操心了。6.3 返工率下降量化返工率可能有點難但有一個簡單的觀測指標合并之后因為代碼規(guī)范問題而被回滾的 PR 數(shù)量。之前平均兩三個 PR 就會有一個因為規(guī)范問題被退回這套體系上線之后最近兩個月基本沒出現(xiàn)過因為規(guī)范問題導(dǎo)致的回滾。這個改善對團隊信任度的提升很明顯大家開始愿意讓 AI 承擔更多的編碼任務(wù)。6.4 這套體系的維護成本當然也要說說不好的方面。規(guī)范的維護是有成本的隨著項目演進規(guī)范文件本身要持續(xù)更新新增依賴時需要評估它是否影響現(xiàn)有規(guī)范CI 腳本如果項目結(jié)構(gòu)調(diào)整了也需要跟著改。好在這套東西的維護頻率不高大概一兩個迭代周期才需要更新一次完全可以接受。7. 關(guān)于AI 生成代碼到底能不能用于生產(chǎn)項目的一點個人體會最后說點個人體會不談方法論。我之前對 AI 生成代碼的態(tài)度經(jīng)歷了一個變化一開始完全不信任后來覺得它寫小段代碼挺好用的但不敢讓它碰大文件直到搭完這套規(guī)范體系我才覺得 AI 代碼生成可以真正進入日常開發(fā)流程。它解決的問題不是AI 能不能寫出正確代碼——這個問題的答案早就人盡皆知了真正的問題是AI 能不能持續(xù)產(chǎn)出符合團隊規(guī)范的代碼。搭規(guī)范的過程本質(zhì)上是把團隊里很多心照不宣的隱性知識顯性化。我自己在寫規(guī)范文件的過程中也重新審視了一遍項目的代碼組織方式發(fā)現(xiàn)了幾個之前一直靠口頭提醒的約定如今終于被寫成了可執(zhí)行的內(nèi)容。這種沉淀即使沒有 AI對項目本身也是有好處的。再分享一個小技巧規(guī)范文件不要一次性寫太長第一次先寫核心的二十條跑兩個迭代之后再根據(jù)實際出現(xiàn)的問題把新的規(guī)則補進去。AI 規(guī)范文件不是法律條文它更像一個項目的操作手冊需要持續(xù)迭代才能貼合項目的真實狀態(tài)。如果你也在糾結(jié)AI 寫代碼靠不靠譜這個問題我建議先把規(guī)范搭起來再說。工具選擇哪家倒是其次真正的核心是你愿不愿意花點時間把團隊里那些默認大家都懂的約定一條一條地寫清楚。條條寫清楚了AI 自然就不會亂來。