资讯详情

Flutter工程规范实战:用Claude Code和Codex约束AI写代码

📅 2026/10/19 3:17:02 | 华诺云谱 👁 阅读
Flutter工程规范实战:用Claude Code和Codex约束AI写代码
最近我在几个 Flutter 项目里试了用 Claude Code 和 Codex 来写代码效果确实惊艳但翻车概率也一点都不低。最典型的情况是AI 能独立完成一个小功能却会在你根本没注意到的地方破坏既有约定——混用状态管理方案、跳过异常处理、把 Model 层的东西直接塞进 Widget、甚至改完业务代码顺手把 pubspec.yaml 里的依赖版本升级了。后来我才想明白问题不在模型不够聪明而在于我没把“工程规范”讲清楚。这篇文章分享的就是我如何用 Claude Code Codex 给 Flutter 项目搭一套工程规范让 AI 从“会写代码”变成“会按规矩写代码”。如果你也在用 AI 辅助 Flutter 开发或者正准备引入这两类工具这应该能帮你少踩不少坑。1. 先想清楚AI 写代码为什么容易“翻车”1.1 翻车的三种典型场景先说说我实际遇到过的翻车案例不是概念层面的猜测都是真实发生过的。第一种是风格不统一。我们项目里明确用 Riverpod 做状态管理结果 AI 在一个新模块里给混用了 setState、Provider、甚至 Bloc 的写法。单看每个文件好像都能跑但如果代码走查的人是你你会疯掉。一个十几万行的项目维护成本就是这么被一点点抬高的。第二种是破坏目录结构。Flutter 项目通常有约定好的分层方式什么文件放 feature、什么放 core、什么放 shared各自有各自的职责。AI 不管这套它倾向于把新代码塞进最靠近调用的地方比如直接把一个数据解析逻辑写在 Widget 文件里。编译没问题但后续换人维护的时候找代码会找到怀疑人生。第三种是引入不必要的东西。AI 觉得某个功能需要国际化支持就顺手改了 pubspec.yaml 加依赖觉得某个功能“用老 API 不优雅”就自作主张换成了新版 API。这种隐性改动最要命因为 code review 的时候如果不逐行 diff根本发现不了。1.2 根因模型对“隐性知识”一无所知为什么 AI 会这样本质原因是工程规范对模型来说是不可见的隐性知识。它不在代码里不在 README 里它存在于团队成员脑子里、在历次 code review 的讨论里、在项目积累的经验里。训练模型的时候它见过大量项目但那些项目的规范各不相同。你给它的上下文如果没有明确说清楚“我们这个项目应该怎么写”它就会用统计学上的“最常见写法”来补全。问题恰恰是最常见写法不等于你的项目应该有的写法。所以我后来总结了一个判断标准只要一个规范没有被写下来、没有被显式放进 AI 的上下文里那 AI 大概率会在某个角落违背它。这不是模型笨而是你的系统设计有缺口。1.3 解法思路把规范显式化想明白根因之后解法思路就很清晰了——把规范变成显式的、可读取的、可强制的信息分三条路落地把规范写进仓库文档让 AI 在每次任务初始就能读到AGENTS.md / CLAUDE.md。把规范写进系统提示词让 AI 在生成代码时持续收到约束。把规范写进自动化检查靠 lint、analyzer、CI 脚本强制保证而不是靠模型自觉。这三条路缺一不可。文档让 AI“知道”提示词让 AI“记住”自动化检查让 AI“不敢忘”。整套组合下来翻车率才会有实际的下降。2. 给 Flutter 项目定义“AI 行为准则”2.1 先写 AGENTS.md再写代码很多团队第一个错误是项目都跑起来了才想起来跟 AI 说“你要遵守我们团队的规范”。这顺序反了。我现在的做法是任何准备接入 AI 辅助开发的 Flutter 项目第一步就是创建一个AGENTS.md放在仓库根目录。这个文件的作用是给 AI 提供一个“项目行为准则”。它类似于团队里新人入职时拿到的那份开发手册只不过读者是一个语言模型。它的内容要覆盖项目技术栈、目录结构、代码风格、状态管理约定、错误处理要求、禁用事项。我这里有一个实际在用的模板可以直接抄# AGENTS.md ## 项目概况 - Flutter 3.xDart 3.x - 状态管理Riverpod禁止使用 setState 处理跨页面状态禁止引入 Bloc/Provider - 网络层Dio 统一拦截器 - 本地存储shared_preferences简单 KV、drift复杂查询 ## 目录结构 - lib/core/通用能力禁止放业务代码 - lib/features/按业务模块划分每个 feature 内部按 data/domain/presentation 分层 - lib/shared/跨模块复用的组件与工具禁止放页面级逻辑 ## 代码风格 - 所有文件名使用 snake_caseWidget 类使用 PascalCase - Widget build 方法内禁止写业务逻辑只允许布局与状态读取 - 所有异步操作必须捕获异常UI 层必须提供失败回退 - 禁止在业务代码中直接使用 print使用 logger ## 完成标准 - 新功能默认不修改 pubspec.yaml如需修改必须单独说明 - Dart 代码必须通过 dart analyze且无新增 warning - 所有新增文件必须放置在约定目录内 - 测试核心业务逻辑必须有 widget test 或 unit test ## 禁止事项 - 禁止升级或降级任何依赖版本 - 禁止修改与当前任务无关的文件 - 禁止自行引入新的第三方库 - 禁止在 UI 线程执行耗时操作这个文件看起来简单但实际效果非常明显。原因在于它把项目里最容易出问题的点都提前讲清楚了AI 不需要从海量代码里反推你的偏好。2.2 目录结构与职责边界AGENTS.md 里我特意把目录结构写得很死因为这是 Flutter 项目里 AI 最容易犯错误的地方之一。很多 AI 生成的代码功能是对的位置是错的。我建议的目录分层是这样的lib/ ├── core/ # 网络、日志、主题、工具类 ├── features/ │ ├── login/ │ │ ├── data/ # repository、dto、local provider │ │ ├── domain/ # 实体、用例、接口 │ │ └── presentation/ # page、widget、controller │ └── home/ ├── shared/ # 通用组件、常量、扩展方法 └── main.dart这套结构其实是很多 Flutter 中大型项目的通用布局。在 AGENTS.md 里写清楚之后还要在系统提示词里再强调一次生成任何文件之前先根据目录职责判断文件应该放在哪里再动笔。实际用下来AI 的“放错位置”概率能明显下降但需要配合系统提示词里的约束而不是只靠文档。后面第三节会细说。2.3 命名、状态管理与错误处理约定如果说目录结构是骨架那命名、状态管理和错误处理就是血肉。这三样东西AI 最容易在细节上翻车。命名方面我见过 AI 生成一堆getUserInfoDataItemContainer()这种嵌套到不知所云的类名也见过它在同一个文件里混用final _controller和final controller。所以规范里必须给死文件用 snake_case、类用 PascalCase、私有变量加下划线、Widget 文件里只能放一个核心 Widget 类其余辅助 Widget 用下划线前缀私有类。状态管理方面我们项目统一用 Riverpod。我在规范里甚至写了一句话如果你觉得某个场景用 setState 更简单那说明你没理解这个项目为什么用 Riverpod。这种略带偏执的表述反而有效因为模型在生成时遇到模糊场景会倾向于选择被明令禁止之外的选项。错误处理是 AI 的另一个重灾区。它的默认行为是写一个 try-catch然后print(e.toString())或者干脆 catch 后什么都不做。这在调试时会让你非常痛苦。规范里应该明确所有 catch 块必须做三件事——记录日志、给用户一个可理解的提示、保证程序状态可恢复。这三条缺一不可。2.4 依赖与版本策略依赖管理是我最激进的一个约束AI 默认没有修改 pubspec.yaml 的权限。任何新增依赖、升级版本、调整约束都必须单独列出由人类确认后再合入。原因很简单AI 生成代码时对依赖的判断是基于统计经验的。它觉得新版好用就升新版觉得某个库合适就引入但它不知道你项目里的兼容性约束、你不知道的历史包袱、团队对某个库的稳定性的偏好。我在实际项目里就遇到过 AI 把 intl 版本抬升了一个大版本结果一排依赖全部冲突治理了整整半天。所以这个策略不是限制生产力而是保护生产力。把依赖变更的开关放在人手里能避免绝大多数由 AI 引发的“连锁灾难”。3. 双工具搭配Claude Code 的“改”与 Codex 的“审”3.1 用 Claude Code 生成与修改代码Claude Code 在代码生成和重构方面有一个很大的优势它支持在启动时读取项目文件把 AGENTS.md 这类约束文档自动纳入上下文。也就是说你不需要每次都在对话里重新粘贴规范它自己会读。我的实际操作流程是这样的在项目根目录启动 Claude Code首次启动时它会自动发现 AGENTS.md并加载到系统上下文里。之后我下任务时只需要把任务描述写清楚同时带上“严格遵循 AGENTS.md 中的约束”这句话。这里有个小技巧不要把任务描述写得像需求文档那么长关键是任务边界要写清楚。我通常写三个要素要做什么、涉及哪些文件、不允许触碰哪些文件。最重要的一个动作是限制编辑范围。Claude Code 支持指定可编辑文件的路径如果你下任务时明确“只允许修改 lib/features/login 目录下的文件”AI 就不会手贱去改 shared 里的公共组件。这一点强烈建议每次任务都显式声明不要因为嫌麻烦而省略。3.2 用 Codex 做独立代码审查如果说 Claude Code 是“写手”那 Codex 在我这里的主要角色是“审查官”。这里有个关键点不要让同一个模型既写代码又审查自己的输出因为思维模式相同容易“自我催眠”。用两个不同的工具交叉验证效果会好很多。我的做法是Claude Code 完成一个功能分支后我把 diff 整理出来丢给 Codex让它做一次独立审查。审查任务是只评不改输出结果按严重程度分三档P0功能逻辑错误可能导致崩溃或数据错误P1违反工程规范会导致维护成本上升P2风格问题不影响功能但不符合项目习惯Codex 特别适合干这件事因为它对代码模式很敏感能快速发现诸如“缺失空安全处理”“未遵循 Riverpod 的听写规则”“catch 块只打印不处理”这类的规范问题。我也试过让它直接修补代码但效果不如让人类来修改、它只做“挑刺”。如果你想复现可以写一个简单的审查提示词模板请对以下 Flutter 代码 diff 进行审查只做审查不做修改。 检查维度 1. 是否遵循状态管理约定Riverpod禁止 setState 跨页共享 2. 是否遵循目录结构约束 3. 是否有未被捕获的异步异常 4. 是否有与任务无关的文件改动 5. 是否存在低质量代码模式硬编码、重复代码、无效空检查 输出格式按 P0/P1/P2 分级列出问题每个问题附上代码位置和修改建议。这个流程走下来能明显减少漏网之鱼。3.3 用 git 分支隔离 AI 改动AI 代码工具另一个让人头疼的问题是它喜欢“顺手”清理它觉得不合理的地方。比如你在任务里让它改一个页面的布局它可能顺手把同文件里另一个函数重命名了。这种“扫地式修改”在 code review 时极难发现。我的解决办法是强制 git 分支隔离。凡是 AI 参与的任务一律开独立分支git checkout -b feat/ai-generate-login-page # 运行 Claude Code 完成任务 git diff main --stat # 检查是否有超出预期的文件变动这一步的核心价值在于所有 AI 的行为都被限制在一个可回滚的容器里。就算它在代码里搞了什么幺蛾子你也可以随时整个分支丢弃不会污染主干。没有这层隔离任何 AI 辅助工具都有潜力变成“生产事故制造器”。3.4 把检查固化成脚本规范只写在文档里靠 AI 自觉那是不牢靠的。最后一道防线是自动化。我把规范里能固化的部分全部写成脚本在 CI 或 git 提交钩子里执行。Flutter 项目里我至少会配三道廉价检查flutter analyze能抓到未使用变量、类型问题、空安全相关错误。dart format --set-exit-if-changed保证代码格式统一这个对 AI 生成代码特别有用AI 经常生成风格不一的格式。一个自定义检查脚本检查是否有 print 残留、是否有 TODO 标记、是否修改了 pubspec.yaml如果修改了则阻断提交提示人工确认。其中一个实际用过的脚本逻辑大概是这样的#!/bin/bash # check_ai_pr.sh # 1. 检查是否修改了 pubspec.yaml若有必须包含 #AI-DEP-REVIEW 标记 if git diff main...HEAD --name-only | grep -q pubspec.yaml; then if ! git log main...HEAD --format%B | grep -q AI-DEP-REVIEW; then echo pubspec.yaml 已变更但提交信息缺少 AI-DEP-REVIEW 标记 exit 1 fi fi # 2. 检查是否引入了 print if git diff main...HEAD | grep -E ^\.*print\( | grep -v kDebugMode; then echo 发现新增的 print 调用请替换为 logger exit 1 fi # 3. 检查空白 diff 之外的意外文件变更交给人工复核 git diff main...HEAD --stat这层自动化脚本实际上是在告诉 AI你可以自由发挥但你的输出必须通过检查。有了这层AI 才会真正“按规矩办事”。4. 落地实操从零搭一套可复制的工程规范4.1 第一步生成项目快照与约束清单在一个已经有一定规模的项目里接入这套规范最难的是让 AI 快速理解项目现状。我建议先让 AI 生成一份“项目快照”再基于快照写规范。项目快照包含三部分项目依赖清单pubspec.yaml 里关键依赖及版本、当前目录结构tree 命令输出、已有代码的风格摘要比如抽取两个典型 feature 的代码片段。拿到快照后让 Claude Code 跑一个任务分析当前项目隐含的代码风格与约定输出一份“项目规约初稿”。这一步相当于让 AI 帮你起草 AGENTS.md而不是从零开始手写。之后人工补充细节和修正偏差效率会高很多。需要注意初稿只能作为素材不能直接拿来用。因为 AI 会根据自己的偏好做“合理推测”而不是基于你团队的真正意图。必须人审一遍把对你来说天经地义的规则写进去。4.2 第二步配置有效的系统提示词AGENTS.md 是静态文档系统提示词是动态约束。两者结合AI 的表现才稳定。我实际使用的系统提示词骨架大致如下你是该 Flutter 项目的资深开发者。项目规范在仓库根目录 AGENTS.md 中你必须严格遵守。 任务执行规则 1. 在生成任何代码前先读 AGENTS.md。 2. 所有新增文件必须放在符合目录约定的位置。 3. 只允许修改与任务直接相关的文件禁止修改无关文件。 4. 必须使用 Riverpod 管理状态禁止引入其他状态管理方案。 5. 所有异步操作必须处理异常UI 必须提供失败回退。 6. 禁止修改 pubspec.yaml除非用户明确要求。 7. 生成完成后检查自己的输出是否符合上述约束并逐条确认。 输出格式 - 先输出你要修改/新增的文件列表 - 再输出每段代码 - 最后输出一份自查清单确认你遵守了哪些规则这套提示词的核心优势是“把约束前置、把检查后置”。前置让 AI 在生成时就有约束后置让它生成后自己检查一遍。实测下来把“自查清单”写进提示词比单纯说一句“请保证代码质量”有效得多。4.3 第三步定义完成标准DoD如果你希望 AI 的输出可直接进入 code review 而不必返工那必须给它一个明确的“完成定义”。没有这个定义AI 会认为自己写完代码就算完成了哪怕代码根本没跑通。我在项目里定义的 AI 任务完成标准是flutter analyze零新增问题新增代码全部落在约定目录内未修改 pubspec.yaml或已明确标注依赖变更原因没有新增print调用所有异步调用已处理异常与失败态相关 widget test 或 unit test 已补充与任务无关的文件没有发生变动这七个条件前五个是可以通过脚本自动检查的后两个需要人眼确认。我把这套标准写在 AGENTS.md 里同时在每次任务的 system prompt 里再贴一遍。实际上有了这套 DoDAI 的输出质量会有一个跳跃式提升。原因不复杂模型在解码时会往“能让用户满意”的方向优化。如果你把“让用户满意”拆解为“通过这七项检查”它就往这七项努力。你若不拆解它就理解为“写一段看起来像代码的东西”。4.4 第四步验证与迭代规范搭好之后不要急着全量铺开。先找一个小任务做“练兵”验证观察 AI 的行为是否符合预期。这一步非常关键能帮你发现规范里的描述漏洞。实际验证时我会故意设置一些“陷阱”任务。比如跟 AI 说“给登录页加一个 loading 状态”但故意不提醒它不要改别的文件。如果它顺手重构了整个 login service说明你的目录边界约束还不够强需要去 AGENTS.md 里补充更明确的措辞。还有一种验证方式是“对抗测试”让 Claude Code 写一个模块再让 Codex 审查这个模块把审查结果反转成规范改进项。比如 Codex 发现“错误处理不统一”说明 AGENTS.md 关于错误处理的描述不够具体Codex 发现“UI 层混入业务逻辑”说明目录职责边界还要再写细一点。规范这种东西从来不是一下子写完的。它应该随项目演进随 AI 工具的能力变化持续迭代。我的经验是每一次 AI 翻车都是一次规范补全的机会。踩过的坑写进规范规范就会变得越来越“坑少”。5. 踩坑记录我在实际项目中遇到的 6 个问题5.1 AI 不理规范直接放飞如果你在任务描述里没有显式提到“遵循 AGENTS.md”AI 是有可能完全不读那个文件的。它默认的行为是“按常识写”而不是“按规范写”。我的解决办法是两条一是在系统提示词里强制“开始任何任务前先读取 AGENTS.md”二是在任务正文结尾再加一句“以上代码必须符合 AGENTS.md 规范”。还有一个隐藏问题即使 AI 读了 AGENTS.md它也可能在生成长代码的中途把约束忘掉。上下文窗口里塞了太多代码早期读到的规范会被挤占。对此我通常把 AGENTS.md 的关键约束在 prompt 里再重复一遍尤其是状态管理和目录结构这两条重要的事情值得反复强调。5.2 文件越改越乱无法追踪有一次我让 AI 修复一个搜索页的 bug它把相关的三个文件的命名、两个工具类的签名、一个 repository 接口全部“顺手优化”了一遍。功能确实修好了但 code review 变成了一场灾难。从那以后我就强制要求每一次 AI 任务都要先在分支上运行并且对最终 diff 做“文件级别”的审查——只允许任务相关文件出现在变更列表里。如果你发现 AI 频繁越界改文件可以在 git 层面做一道硬拦截用一个 pre-push 脚本检查变更文件列表如果包含“禁止清单”里的文件直接阻断提交。5.3 上下文太长导致费用飙升我一开始喜欢把所有相关代码都粘贴给 AI让它一次性搞定结果上下文越长token 消耗越快费用也越夸张。后来我学到一个技巧先让 AI 去定位“问题可能出在哪个文件”再决定给它哪些代码片段。也就是把“大而全的一次性请求”改成“逐步收敛的短对话”。另一个控制费用的办法是在项目里维护一个“代码索引”文件记录每个 feature 的关键文件路径AI 在提问之前先读索引而不是把整个项目都塞进上下文。这个办法在大型 Flutter 项目里效果尤其明显。5.4 生成的代码“最优美但不适用”AI 有一个倾向写出结构非常优雅、抽象层次很高的代码。问题是这种代码在你的团队里可能过于超前别人维护起来反而困难。比如它可能会给你生成一个“抽象工厂 策略模式”的架构而你的项目只需要一个简单的 if-else 判断。我的解决方式是不是禁止 AI 做高层次的抽象而是在规范里写明“优先选择与项目现有代码风格一致的实现方式禁止引入新的设计模式与架构范式除非单独说明并经过确认”。这条约束能避免 AI 输出“象牙塔”式的代码。5.5 老项目里 AI 的表现更差如果你的 Flutter 项目历史悠久里面可能充满了历史遗留的奇怪命名和不一致的风格AI 在这样的项目里表现反而更差因为它会在新旧风格之间摇摆。我的建议是先不要在全量项目里用 AI而是挑一两个结构还算规整的 feature 模块优先在这些模块里接入 AI 辅助。等到模块规范稳定了再让 AI 帮你把其他模块逐步重构对齐。这个过程要慢要有耐心。规范覆盖不全的部分AI 就更容易翻车这很正常。5.6 AI 的“假完成”这是最隐蔽的一个坑AI 可能在输出里写了一个“测试”但实际上这个测试什么也没验证它可能引用了某个工具函数但那个函数根本不存在它可能把新页面接入了路由但忘了在页面里注册依赖。应对这个问题的杀手锏是构建。所有的代码审查都不如让代码真正跑一遍。Claude Code 完成任务后我会立刻执行flutter analyze flutter test然后跑一次相关页面的 smoke test。如果这两个坎过不了不管 AI 说它完成了什么统统打回重做。6. 一些收尾的实际体会整套规范落地之后我最直观的感受是AI 的输出不再是“能跑就行的草稿”而是“可以直接走 review 流程的代码”。在没有规范的时候我拿到 AI 生成的一千行代码大概要花两个小时改造成符合项目风格的东西有了这套流程之后修改时间压缩到了半小时以内剩下主要是在审查边界情况和补测试。如果你要开始做这件事我建议从最小闭环开始先把 AGENTS.md 写出来再指定一个小 feature 让它实现然后立刻跑 analyze 和人工 diff。不要一开始就追求全项目覆盖那段时间和精力成本太高了。有一个小技巧值得分享把 AGENTS.md 放在仓库根目录的好处不只是给 AI 看也是给所有人看。当团队成员写代码风格不一致时这个文件也能起到“仲裁”作用。很多时候工程规范的价值不仅在于约束 AI还在于帮助人类达成共识。用 AI 来倒逼团队把规范补齐这件事本身就很有意义。另外Claude Code 和 Codex 这两款工具的能力也在快速迭代我目前这套流程未必是最优解但“显式化规范 约束上下文 自动化检查”这套思路我认为短期内不会过时。无论工具怎么变模型始终需要一个清晰的“边界说明”而这个边界就是我们团队工程素养的体现。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑