1. 为什么“让 AI 先写”几乎总是最慢的路径
我做了十多年全栈,从 jQuery 时代一路写到现在的 AI 辅助开发,见过太多团队在引入 AI 编程工具之后,第一周效率暴涨、第三周开始返工、第二个月直接把 AI 生成的代码全部推翻重写。问题从来不在模型能力上,而在于协作纪律。标题里说的“别急着让 AI 写代码”,不是反对用 AI,而是反对把 AI 当成一个“你说一句它吐一坨”的黑盒。全栈工程师的战场横跨前端、后端、数据库、部署、调试,任何一环的上下文丢失,都会让 AI 生成的代码变成技术债的加速器。
先把这个结论摆出来:AI 写代码的质量,取决于你给它的约束质量,而不是取决于模型参数有多大。我实测过同一个需求,用两种方式让 AI 生成:第一种是直接丢一句“帮我写一个用户登录接口”,第二种是先写清楚技术栈、目录结构、错误码规范、鉴权方式、返回体格式,再让它写。第一种出来的代码能跑,但字段命名和项目里其他模块完全对不上,错误处理是它自己编的一套;第二种出来的代码,我基本只改了两三个变量名就合并了。差距不在 AI,在于我有没有先做“纪律”这件事。
这篇内容适合三类人:一是刚把 AI 编程工具接进日常工作的全栈工程师,二是带团队、需要制定 AI 协作规范的技术负责人,三是还在观望、想知道“别人到底怎么用才不出事”的开发者。我会把五条纪律拆开讲透,每一条都配上我踩过的坑和具体的落地做法。关键词里提到的 CLAUDE.md、Claude Code、Codex 这些工具,我会在对应纪律里自然带出来,讲清楚它们在纪律体系里扮演什么角色,而不是单独做工具测评。
有一点必须先说清楚:纪律不是限制你,而是让你少返工。我见过最惨的一次,是一个同事让 AI 一口气生成了整个订单模块,两千多行,跑起来没问题,但里面用了三种不同的日期格式、两套日志方案、四个重复的工具函数。后来光是对齐这些,花的时间比手写还多。这就是没有纪律的代价。
2. 纪律一:先立契约,再让 AI 动笔
2.1 契约到底是什么,为什么它比提示词更重要
很多人把精力全花在“怎么写出更好的提示词”上,却忽略了一个更根本的东西:契约。契约不是提示词,它是你和 AI 之间关于“这个项目长什么样”的共识文件。提示词是临时的、一次性的,契约是持久的、可复用的。在全栈项目里,契约至少包含四层:技术栈与版本、目录与模块边界、命名与代码风格、接口与数据格式。
我现在的习惯是,任何新项目在让 AI 写第一行代码之前,先花二十分钟写一份契约文件。这份文件不需要多漂亮,但必须具体。比如技术栈不能只写“React”,要写“React 18 + TypeScript 5 + Vite,状态管理用 Zustand,不用 Redux”;目录结构不能只写“前后端分离”,要写清楚src/api放请求封装、src/features按业务域拆分、server/routes放路由、server/services放业务逻辑。这些细节看起来啰嗦,但正是它们决定了 AI 生成代码能不能直接融进项目。
这里就要提到 CLAUDE.md 这类文件的价值了。它的本质就是一份放在项目根目录的契约,AI 工具在读取项目时会优先加载它。我见过很多人装了 Claude Code 之后,第一件事是研究怎么让它跑起来,却从来没想过在项目里放一份 CLAUDE.md。结果就是每次对话都要重新解释一遍项目背景,AI 每次生成的风格都不一样。把契约写进 CLAUDE.md,等于给 AI 装了一个“项目记忆”,它每次动手前都会先读这份约束。
2.2 契约里必须写死的五类信息
我把契约里必须写死的信息归成五类,缺一类都会出问题。第一类是技术栈与版本号,因为 AI 默认可能用旧版本 API,比如它可能给你写 React 类组件,而你的项目全是函数组件加 Hooks。第二类是目录与文件职责,不写清楚,AI 会把工具函数塞进组件文件里。第三类是命名规范,比如接口返回字段统一用 camelCase 还是 snake_case,数据库字段用不用下划线,这些不统一,前后端联调就是灾难。第四类是错误处理与日志规范,AI 默认的错误处理往往很随意,要么直接console.log,要么抛一个没有错误码的异常。第五类是依赖白名单,明确告诉 AI 哪些库可以用、哪些不许引入,否则它会给你装一堆你根本没打算用的包。
我举个真实例子。之前做一个后台管理系统,契约里写了“所有请求走src/api/request.ts封装,禁止在组件里直接调 axios”。结果有个新同事没看契约,直接让 AI 写了一个页面,AI 果然在组件里直接import axios发请求。代码能跑,但绕过了统一的鉴权拦截和错误提示,上线后用户 token 过期时页面直接白屏。后来我们把这条规则加粗写进 CLAUDE.md 顶部,类似问题再没出现过。这就是契约的作用:它把“大家都知道但没人写下来”的隐性规则,变成 AI 也能遵守的显性约束。
2.3 契约的维护节奏:什么时候更新,谁来更新
契约不是写完就锁死的。项目在演进,契约也要跟着更新。我的做法是:每次引入新依赖、新增一个业务域、调整一次目录结构,就顺手更新契约文件。更新的人就是做这次改动的人,不另设“契约管理员”,否则一定会滞后。更新的时候有个小技巧:在契约文件里用一个“变更记录”小节,简单记一行“某月某日,新增支付模块,引入 xxx 库”,这样 AI 读到的时候能感知到项目的最新状态。
还有一点,契约要短。我见过有人把契约写成了一本手册,几千字,结果 AI 读取时反而抓不住重点。我的经验是控制在两三百行以内,用列表和表格,把最关键的约束放前面。真正复杂的业务逻辑,不该塞进契约,而应该放在代码注释或单独的文档里,让 AI 按需读取。契约的定位是“宪法”,不是“百科全书”。
3. 纪律二:把任务切到 AI 能一次吃下的粒度
3.1 全栈任务为什么天然容易“喂太大”
全栈工程师最容易犯的错,就是习惯性地把一整条链路当成一个任务。比如“做一个商品详情页”,这句话背后其实包含了:前端页面渲染、路由参数解析、接口请求、后端查询、数据库联表、缓存策略、错误兜底。你把这整句话丢给 AI,它要么只做前端给你一个假数据页面,要么前后端都写但两边对不上。这不是 AI 笨,是任务粒度太大了。
我做过一个对比实验。同一个“商品详情”需求,第一种方式一次性丢给 AI,它生成了前端组件、一个 mock 数据文件、一个后端路由,但前端请求的字段名和后端返回的字段名对不上,图片字段一个叫imageUrl一个叫img_url。第二种方式我拆成四步:先让它根据数据库表结构写后端查询接口,确认返回体;再让它根据返回体写前端类型定义;再写请求封装;最后写页面组件。四步下来,几乎没有返工。差别就在于粒度。
3.2 一个可复用的任务切分模板
我现在切任务基本遵循一个模板:数据层 → 接口层 → 类型层 → 视图层 → 交互层。数据层是数据库查询或数据转换,接口层是路由和请求处理,类型层是前后端共享的类型定义,视图层是页面或组件渲染,交互层是事件、状态和副作用。每一步都单独和 AI 对话,每一步的产出都作为下一步的输入。
这个模板的好处是,每一步的产出都是可验证的。数据层写完,我可以直接跑一个查询看结果对不对;接口层写完,我可以用 curl 或 Postman 测一下;类型层写完,TypeScript 编译能过就说明对上了;视图层和交互层写完,页面能跑就基本没问题。如果一次性让 AI 写完整条链路,中间任何一环错了,你都要在两千行代码里找那一行。
这里要提一下 Codex 这类工具的使用场景。Codex 在补全和单文件生成上很强,但它不适合处理跨文件的大任务。我通常用它来做“类型层”和“视图层”这种边界清晰、上下文集中的活,而把“数据层”和“接口层”交给能读取整个项目上下文的工具,比如 Claude Code。工具没有绝对好坏,关键看任务粒度和它擅长的边界是否匹配。
3.3 切分之后,怎么给每一步写“验收标准”
光切分还不够,每一步都要有验收标准,否则 AI 交出来的东西你还是不知道对不对。我的做法是,在给 AI 下指令的时候,顺手把验收标准也写进去。比如让它写后端查询接口,我会写“返回体必须是{ code, data, message }结构,data 里包含 id、name、price、imageUrl 四个字段,price 是数字类型,imageUrl 是完整 URL”。这样 AI 生成完,我一眼就能对照检查。
验收标准还有一个隐藏作用:它逼着你在动手前就想清楚需求。很多时候我们觉得“让 AI 写就行了”,其实是因为自己也没想清楚要什么。把验收标准写出来的过程,就是逼自己把模糊需求变具体的过程。我见过太多返工,根源不是 AI 写错了,而是人自己没想清楚,AI 只是把这种模糊放大了。
4. 纪律三:让 AI 先读代码,再写代码
4.1 “凭空生成”是返工的最大来源
AI 编程工具最诱人的地方,就是它能凭空生成代码。但恰恰是这个“凭空”,埋了最大的雷。因为你的项目里已经有了一套约定俗成的写法:工具函数放在哪、请求怎么封装、状态怎么管理、组件怎么拆分。AI 不知道这些,它只会按它训练数据里最常见的写法来生成。结果就是新代码和旧代码风格割裂,维护成本飙升。
我踩过最典型的一次坑,是让 AI 写一个表单校验。它生成了一套基于yup的校验方案,但我们项目里统一用的是zod。代码能跑,但项目里从此多了一套校验库,打包体积大了,团队里两个人维护两套校验逻辑。后来我强制要求:任何 AI 生成代码之前,必须先让它读一遍项目里已有的同类实现。
4.2 怎么让 AI “读代码”:三种可操作的方式
第一种方式是显式引用文件。在对话里直接告诉 AI“参考src/utils/validate.ts的写法,实现一个新的校验函数”。大多数支持项目上下文的工具都能读取指定文件。第二种方式是让它先总结再动手。我会说“先读src/api目录下的所有文件,总结出请求封装的模式,然后再按这个模式写一个新的接口调用”。这样它输出的总结本身就是一次校验,我能看出它有没有理解对。第三种方式是用契约文件兜底。如果项目太大,AI 读不完,就在 CLAUDE.md 里写清楚“所有请求必须走 request.ts,所有校验必须用 zod”,让它至少知道边界在哪。
这三种方式我通常组合使用。先靠契约兜底,再显式引用关键文件,最后让它总结确认。三步下来,AI 生成的代码基本能无缝融入项目。这里有个细节:让 AI 读代码的时候,不要一次让它读太多。我试过让它读整个src目录,结果它抓不住重点,总结得乱七八糟。后来我改成一次只让它读一个模块,比如“只读src/features/order下的文件”,效果立刻好了很多。
4.3 读完之后,让它“复述约定”再动手
这是一个我强烈推荐的习惯:AI 读完代码后,不要立刻让它写,先让它用几句话复述它理解的约定。比如“我理解这个项目的请求封装是这样的:所有请求走 request.ts,自动带 token,错误统一弹 toast,返回体解包后直接给业务层”。如果它复述对了,再让它写;如果复述错了,说明它没读明白,这时候纠正比写完再改成本低得多。
这个习惯还有一个好处:它把 AI 从“生成器”变成了“协作者”。生成器是你给指令它给结果,协作者是它先确认理解再动手。后者出错率低得多。我现在带新人也是这个逻辑:先让他读代码,复述一遍,确认理解对了再动手。AI 和人在这件事上没有本质区别。
5. 纪律四:AI 写的每一行,你都要能解释
5.1 “能跑就行”是全栈工程师最危险的幻觉
我见过太多人,AI 生成代码后跑一下,页面出来了、接口通了,就直接合并。这种“能跑就行”的心态,在 AI 时代特别危险。因为 AI 生成的代码往往“看起来对”,但里面可能藏着性能问题、安全问题、边界条件缺失。你如果解释不了这行代码为什么这么写,那你就没有真正拥有它,出了问题你也不知道从哪查。
举个我亲历的例子。一个同事让 AI 写了一个分页查询,代码跑起来没问题,数据也返回了。但上线后数据量一大,接口响应从 200ms 涨到 8 秒。后来排查发现,AI 写的是先查全量再在内存里分页,而不是数据库层面分页。代码“能跑”,但完全不能用。如果当时他多问一句“这个分页是在数据库做的还是内存做的”,就不会有这个事故。
5.2 解释的四个层次:从“知道它干嘛”到“知道它为什么这么干”
我要求自己对 AI 生成的代码至少能解释四层。第一层是功能层:这行代码在做什么。第二层是数据层:它操作了哪些数据,数据从哪来、到哪去。第三层是边界层:空值、超长、并发、异常情况下它会怎样。第四层是取舍层:为什么用这个方案而不是另一个,比如为什么用Map不用对象,为什么用防抖不用节流。
这四层里,最容易忽略的是边界层和取舍层。功能层和数据层看一眼就懂,但边界和取舍往往藏着坑。我现在养成一个习惯:AI 生成代码后,我会刻意问它“如果输入是空数组会怎样”“如果两个请求同时到达会怎样”。它的回答如果含糊,我就自己补测试。这个过程很烦,但比上线后半夜被叫起来排查强。
5.3 解释不了怎么办:三个处理动作
如果某段 AI 生成的代码我解释不了,我有三个动作。第一,让它自己解释,问它“这段代码为什么这么写,有没有更简单的写法”。第二,查文档,尤其是涉及新 API 或新库的时候,AI 可能用了过时或错误的用法。第三,重写,如果解释完还是觉得别扭,就自己重写一遍,哪怕功能一样。重写的过程就是理解的过程,而且重写后的代码往往更贴合项目风格。
这里要提醒一句:不要因为“AI 写的”就降低标准。我见过有人对 AI 生成的代码特别宽容,觉得“它能写出来就不错了”。这种心态要不得。AI 是你的工具,不是你的背锅侠。代码合并进主干,署名是你,出了问题也是你负责。所以解释不了就别合并,这是底线。
6. 纪律五:把 AI 的产出纳入版本控制与评审
6.1 AI 生成的代码,提交信息要写清楚
很多人用 AI 写完代码,git commit的时候随手写个“update”或者“fix”。这在 AI 协作场景下是灾难。因为三个月后你回头看,根本分不清哪些代码是 AI 生成的、当时为什么这么写。我的做法是,提交信息里明确标注 AI 参与的部分,比如“feat: 订单列表接口(AI 生成初稿,人工调整分页逻辑)”。这样回溯的时候一目了然。
更进一步,我会在提交信息里写清楚“人工改了什么”。比如“AI 生成的分页是内存分页,已改为数据库分页”。这条信息在 code review 的时候特别有用,评审的人能直接看到 AI 的原始产出和人工修正的差异,从而判断修正是否合理。这比看最终代码更有价值,因为最终代码看不出修改痕迹。
6.2 评审 AI 代码,重点看什么
评审 AI 生成的代码,和评审人写的代码,重点不一样。人写的代码,重点看逻辑和边界;AI 生成的代码,我重点看四样东西。第一是一致性:命名、目录、错误处理是否和项目其他部分一致。第二是依赖:有没有引入不该引入的库。第三是边界:空值、异常、并发有没有处理。第四是冗余:有没有重复造轮子,项目里已有的工具函数它是不是又写了一遍。
这四样里,冗余是最隐蔽的。AI 不知道你项目里已经有一个formatDate函数,它可能又给你写一个。单个看没问题,积累多了项目里就有五六个功能重复的工具函数。我现在的做法是,评审时先搜一遍关键函数名,看有没有重复实现。这个动作花不了几分钟,但能省下大量后期重构的时间。
6.3 建立“AI 代码回滚”的预案
最后一条,也是最少人做的一条:给 AI 生成的代码准备回滚预案。AI 生成的代码有时候会引入一些隐蔽的问题,上线后才发现。这时候如果没法快速回滚,就只能热修复,风险更大。我的做法是,AI 参与度高的功能,单独拆成一个提交或一个分支,上线后观察一段时间再合并主干。这样出问题可以直接回滚,不影响其他功能。
这个做法听起来保守,但在 AI 协作场景下特别必要。因为 AI 生成的代码,你对它的信任度天然低于自己手写的代码。既然信任度低,就要用工程手段兜底。回滚预案不是不信任 AI,而是承认 AI 的不确定性,用流程来对冲。我实测下来,这个习惯让我在两次 AI 代码引发的小故障里,都在五分钟内完成了回滚,没有影响到用户。
7. 我在实际协作中总结的几条补充心得
7.1 工具选型:别追新,追匹配
关键词里提到的 Claude Code、Codex 这些工具,我都用过。我的体会是,工具没有绝对的好坏,关键看它和你的任务粒度、项目规模是否匹配。Claude Code 适合读取整个项目上下文、处理跨文件任务;Codex 适合单文件补全和边界清晰的生成。我现在的组合是:大任务用能读项目的工具,小任务用补全工具,两者不冲突。
还有一点,工具装好之后,第一件事不是写业务代码,而是拿一个已有模块做实验。让它读一遍、复述一遍、改一个小功能,看它的输出风格和项目是否匹配。这个实验花半小时,但能帮你判断这个工具适不适合当前项目。我见过有人装完工具直接上生产代码,结果风格完全不搭,返工了一周。
7.2 心态:AI 是副驾驶,不是自动驾驶
最后说心态。标题说“别急着让 AI 写代码”,核心不是技术,是心态。AI 是副驾驶,它能帮你导航、帮你观察、帮你处理一些重复操作,但方向盘在你手里。你如果把它当自动驾驶,闭眼让它开,出事是迟早的。全栈工程师的价值,不在于写代码的速度,而在于对系统的理解和判断。AI 能加速写代码,但替代不了理解。
我现在的日常是:AI 写初稿,我改逻辑;AI 写重复代码,我定架构;AI 查文档,我做决策。这个分工下,我的产出比纯手写快了不少,但质量没有下降。关键就在于我没有把判断权交出去。每一条纪律,本质上都是在守住这个判断权。契约是判断权的显性化,任务切分是判断权的粒度化,读代码是判断权的上下文化,能解释是判断权的验证化,版本控制是判断权的兜底化。五条纪律,一个核心:你始终是那个负责的人。
踩过几次坑之后,我越来越觉得,AI 协作最难的不是技术,是克制。克制住“让它一口气写完”的冲动,克制住“能跑就行”的侥幸,克制住“追新工具”的焦虑。把这几条纪律坚持下来,AI 才真正成为你的助力,而不是你的技术债来源。