模块五 · AI 协作与表达
AI 协作与代码评审:把生成变成产出
生成成本掉到接近零,审查、验证与责任一分没少。这一节给出上下文四件套、任务拆解粒度、读 diff 的四个检查面,以及一份能算清"到底省了多少"的账。
读完这一节,你能做到
- 说清 AI 改变了哪一步、没改变哪一步,不再用"效率提升"这种没口径的说法。
- 用「目标 / 现状 / 约束 / 验收」四件套写出一段 AI 真能用的上下文。
- 把一个大需求拆成"一次 code review 能看完"的粒度,并排出正确顺序。
- 用四个检查面审 AI 写出的 React 代码,当场指出它错在哪。
- 用指标算清一次 AI 协作是净赚还是净亏。
一、AI 改的是生成成本,不是判断责任
先把边界划清,否则后面所有结论都会跑偏。
| 环节 | 以前 | 现在 | 变了吗 |
|---|---|---|---|
| 写出第一版 | 30-90 分钟 | 几十秒 | 变了,缩了两个数量级 |
| 读别人的代码 | 有注释可循 | 无上下文、风格随机、量更大 | 变难了 |
| 验证是否真的对 | 自己写的,心里有数 | 不知道它省略了什么 | 变难了 |
| 出事谁负责 | 作者 | 还是作者 | 没变 |
| 需求判断、边界取舍 | 人 | 还是人 | 没变 |
由此得到三条工作纪律:
- AI 负责"给出一个候选实现",你负责"证明它对这个场景是对的"。 候选可以错,证明不能缺。
- 生成量越大,审查成本越高。 一次让 AI 写 800 行,你审不动,等于把 bug 直接合进主干。
- 不上测试的 AI 代码不是快,是提前透支。 上线后排查的时间,一定大于当时省下的时间。
一句话:AI 让"写"接近免费,于是"判断"变成唯一的成本项。 这也是这一节要练的唯一能力。
二、上下文是新的需求文档
模型只看得到你给它的东西。它不知道你的 React 版本、你的目录约定、你不许引入 lodash、你的列表页要支持服务端分页。你不说,它就按训练数据里最常见的写法猜——最常见不等于适合你。
上下文四件套,缺一件就会返工:
- 目标:要解决谁的什么问题,验收时看哪个页面/哪个行为。
- 现状:React 与依赖版本、相关文件路径、现有实现长什么样、已有约定(状态放哪、请求走哪个 Hook)。
- 约束:不许新增依赖、不许改动公共组件、必须用 TS 严格模式、错误展示要复用现有
ErrorBanner。 - 验收:什么算完成。要具体到"输入 2 个字不发请求、清空后列表恢复全量、请求失败显示重试按钮"。
反例,也是绝大多数人的起手式:
帮我写一个订单列表页,要分页和搜索。
它多半会给你一个自带 useState + useEffect + fetch + 手写 loading 的组件。语法没错,但和你项目里 TanStack Query 的约定冲突,你需要几乎重写。不是它不行,是你没告诉它规则。
正例(同一件事,多写四行):
目标:订单列表页,支持关键词搜索与分页。
现状:React 19 + TS 严格模式;请求用 @tanstack/react-query,
列表已有 <OrderTable />(src/components/order-table.tsx),
项目里没有 axios,用 fetch 封装在 src/lib/http.ts。
约束:不新增依赖;搜索词进 URL query;不改 OrderTable 的 props 签名。
验收:输入停顿 300ms 才发请求;翻页保留旧数据;失败显示重试按钮;空结果有空态。
同一个模型,同一件事,产出质量差一个数量级。上下文不是礼貌寒暄,它是接口定义。
三、拆任务:一次只让它做一件可审的事
判断粒度只看一个标准:这次 diff 我能不能在 10 分钟内逐行看完并说清每块为什么这么写。 看不住,就再拆。
推荐顺序,因为它让每一步都能被上一步约束住:
① 定类型/接口 → ② 写最小实现 → ③ 补测试与边界 → ④ 重构与命名
- 先定类型,AI 后面写的实现就被类型约束住了,幻觉字段会立刻报错。
- 先写最小实现,再补边界,比"一次要求写完所有异常分支"更容易审。
- 最后单独做一轮重构,不要和功能混在同一轮,否则 diff 里两种意图搅在一起,无法判断。
一个真实的反例:让 AI「一次做完列表页 + 搜索 + 分页 + 错误处理 + 单测」,产出 460 行。你审了 40 分钟,发现它把分页状态放进了本地 state 而不是 URL,导致刷新丢失;测试是对着自己写的 mock 测的,改实现就红。返工重来。拆成四轮,总耗时反而少。
四、读 diff 的四个检查面
按顺序检查,前一项不过就不用看后一项。
第一面:正确性(边界与竞态)
AI 写的 React 代码,错误高度集中在几个地方:
- 列表
key用下标,遇到插入、删除、排序就状态错位。 useEffect依赖漏项,或为了消警告加eslint-disable而不是改成函数式更新。- 请求竞态:先发的请求后返回,覆盖了新数据。它通常不写
AbortController或忽略标记。 - 卸载后
setState:卸载时没清理定时器、订阅或未完成的then。 - 把可派生的值放进
state:useState(props.value * 2)这类,后续不同步,成为最难查的 bug。 - 受控组件只给
value不给onChange,输入框直接变只读。
第二面:依赖与真实性(幻觉)
AI 会编出"看起来很像"的 API。审的时候逐项确认:
- 它 import 了包里不存在的导出吗?版本没装的属性吗?
- 它用的 API 在你的版本里存在吗?(
use、useActionState、useOptimistic都是 React 19 才有) - 它悄悄新增了依赖吗?
package.json有没有被动过? - 它引用的文件路径、组件名,项目里真的存在吗?
第三面:一致性
命名、目录、错误处理方式、请求层、样式方案,和你项目现有代码是否同一套。风格分裂的代价不是难看,是下一个改这块的人要多读两套心智模型。
第四面:可测性
副作用有没有隔离出来?能不能在不 mock 组件内部的前提下写测试?如果它把请求、状态、渲染全揉在一个 200 行组件里,就是不可测,要求它拆。
四个面过完,你才拿到一个"可提交"的候选,而不是"能跑"的惊喜。
五、验证闭环:让"能跑"变成"有基线"
AI 写得越快,验证就是唯一的瓶颈。三道闸,按成本从低到高:
- 静态闸:
tsc --noEmit+ ESLint + 构建。能拦住幻觉 API、类型不匹配、未使用变量,几十秒。 - 行为闸:关键路径的单测/组件测试,外加一次真实手测。测试要测行为而不是实现——否则 AI 改实现你就得改测试。
- 指标闸:这次改动有没有把某条线弄坏。至少看两个数:渲染次数或交互延迟(用 Profiler / Performance 面板)、首屏或包体积(用构建产物对比)。
第三道闸最容易被跳过,也最容易在面试里被追问。"性能没变差"不是感觉,是前后两个数。
六、算例:同一个任务的两种做法
任务:给订单列表加"标记完成",要求乐观更新、失败回滚。以下时长为示意值,用来说明记账方式,不是行业基准。
做法 A:一把梭
| 项 | 耗时 |
|---|---|
| 写提示词、等生成 | 3 分钟 |
| 逐行 review 180 行 | 25 分钟 |
| 发现分页状态放错位置,返工 | 30 分钟 |
| 手测时发现失败不回滚,再改 | 20 分钟 |
| 合计 | 78 分钟 |
做法 B:拆四轮
| 轮次 | 交给 AI 的部分 | 自己做的部分 | 耗时 |
|---|---|---|---|
| ① 类型 | mutation 的入参与返回 | 定接口 | 8 分钟 |
| ② 实现 | 乐观更新的骨架 | 补 onError 回滚与 cancelQueries | 12 分钟 |
| ③ 测试 | 用例骨架 | 补失败路径断言 | 15 分钟 |
| ④ 重构 | 抽 Hook | 命名与目录归位 | 10 分钟 |
| 合计 | 45 分钟 |
净收益公式:
净收益 = 生成节省的时间 − (审查时间 + 返工时间 + 线上事故成本)
做法 A 的"生成节省"更夸张,但审查与返工把它吃干净了;做法 B 把 AI 放在"产出候选"的位置,把判断留在自己手里,所以总额更小、风险更低。
还有一个隐性收益:做法 B 里那 15 分钟写测试、10 分钟改命名,是你在长本事;做法 A 里那 25 分钟 review 加 50 分钟返工,多半只是在收拾别人(模型)的烂摊子。
七、常见坑
- 用 AI 的答案去面试。 背下来的答案经不起一层追问:你说"快照",他问"那为什么函数式更新能读到新值",立刻断线。用 AI 学可以,用 AI 答不行。
- 把它当编译器用,不当实习生用。 实习生要交代背景、要给验收标准、要抽查;AI 同理,只给一句话就只能给你通用答案。
- 幻觉 API 直接上生产。 不存在的参数在开发环境可能静默失效,到线上才炸。
- 依赖被悄悄改动。 合代码前必看
package.json与 lock 文件有没有变。 - 测试自己骗自己。 让 AI 写测试时,它常对 mock 断言而不对行为断言,实现一改就红、bug 一留就绿。
- 把业务数据贴进去。 订单号、用户手机号、内部接口地址、未公开的费率,都不该出现在公网模型里;脱敏或改用私有部署。
- 每人一套写法。 团队没有统一约定时,AI 会按每个人各自的提示词生成各自的风格,三个月后没人认得出这是同一个项目。
八、面试怎么讲
面试官现在稳定会问:"你平时怎么用 AI 写代码?"他不想听工具名,他想确认三件事:你有没有判断、有没有验证、有没有踩过坑。
四步答法:
怎么用:拆任务(类型 → 实现 → 测试 → 重构),一次只让它产出一屏能看完的 diff
怎么验:tsc + 单测 + 指标前后对比,三道闸都过才合
踩过什么坑:它给列表 key 用了下标,插入后输入框内容错位;后来把 key 规则写进团队约定
换来什么:同类任务从 78 分钟降到 45 分钟,返工率从"每两次一次"降到"每五次一次"
三个不要:不要说"提升 50%"却没有口径;不要说"全交给 AI"(等于自曝没有审查);不要贬低工具(显得没用过)。
反问环节可以问:"团队对 AI 生成的代码有 review 约定吗?"——这个问题会立刻把你和"只会喊 AI 提效"的候选人区分开。
九、动手任务:自写 → AI 写 → 对比 → 取舍
每题四步,不许跳步:① 自己写(限时,关补全)→ ② 让 AI 写同一题 → ③ 逐条比对 → ④ 说出保留谁、为什么。
① 自己写
- 写一个订单列表的搜索框:输入停顿 300ms 才发请求,清空恢复全量,请求失败显示重试。(限时 20 分钟)
- 写一个
useOrdersHook,支持分页与取消上一次请求,卸载时不再setState。
② 让 AI 写同一题
把上面的题干原样交给 AI,补一句约束:React 19 + TS 严格模式,不新增依赖,不改现有组件 props。要求它标注"哪里不确定"。
③ 对比
| 检查面 | 我的版本 | AI 版本 | 差异说明 |
|---|---|---|---|
| 正确性(竞态/清理/依赖) | |||
| 依赖与真实性(幻觉 API) | |||
| 一致性(命名/目录/错误处理) | |||
| 可测性(能否不 mock 内部) |
④ 说取舍
- AI 版本里有没有你没一眼看出来的 bug?是竞态、清理,还是依赖?
- 它"多写"的部分里,哪些是该删的样板?
- 这段代码进 review,你会提哪三条意见?
- 下次同类任务,哪一步你会让它做、哪一步坚持自己写?
记录模板(一题一行,攒够 10 行就是你面试的案例库):
题目 | 我错在哪 | AI 错在哪 | 最终保留谁的写法 | 用时(我/AI/合计)
十、自测
- AI 让哪一步的成本发生了数量级变化?哪一步没变?
- 上下文四件套是哪四件?缺哪一件最容易返工?
- 判断任务拆分粒度的标准是什么?
- 读 diff 的四个检查面按什么顺序做?为什么先看正确性?
- AI 写的 React 代码,错误集中在哪几类?
- 净收益怎么算?为什么"生成越快越赚"这个直觉是错的?
参考答案要点:生成;审查、验证与责任。目标/现状/约束/验收,缺"约束"。一次 review 能在 10 分钟内逐行看完。正确性 → 依赖真实性 → 一致性 → 可测性,因为后面的检查在不正确的代码上没意义。key 用下标、依赖漏项、请求竞态、卸载后 setState、派生值进 state、受控组件缺 onChange。净收益 = 生成节省 −(审查 + 返工 + 事故),生成越快审查量越大。
配套题目
★ 越多越该先背;点题目可看完整答案与深入版。
- ★★★★★ AI 生成的 React 代码,你会怎么审?
- ★★★★☆ 怎么给 AI 写上下文,才能一次要出能用的代码?
- ★★★★☆ AI 写的代码质量怎么保证?你的验证流程是什么?
- ★★★★☆ 哪些活你会交给 AI,哪些坚持自己写?
- ★★★☆☆ AI 时代的性能优化,做法有什么变化?
- ★★★★☆ AI 生成的测试为什么经常"假绿"?
提示:读完可以顺手做几道对应题目,检验是否真的理解