速查版:一屏看懂怎么审核 AI 交付的代码。你不是要变成程序员,是要能判断 AI 给的东西对不对——四步验收法 + 危险信号清单 + 15 分钟最小可行验收,看完就能自己上手。
先说事实,不美化:
你全盘 13 个有效项目(package.json 命中 18 个,对应 13 个独立项目),其中 36 个 Express 包——全部是 AI 生成的。技术栈分布也是实测的:express 85 处、react 85 处、vue 74 处、fastapi 64 处、better-sqlite3 26 处、ffmpeg 67 处、sharp 24 处,zod 9 处、drizzle-orm 7 处、jsonwebtoken 9 处,playwright 1.63。
真实收益:能快速出活。13 个项目、几十个后端包,一个人跑完,没有这些不可能。
真实风险:讲不出为什么这样写,出问题只能从头猜。
*.test.* 和 *.spec.* 全盘 0 个命中。唯一的例外是 delimao-system 的 packages/engine/test/run.ts——用 node:assert 手写了 6 个型号的断言。这是 AI 生成代码最大的结构性漏洞:代码能跑,但没人知道它是不是对。改动之后没人知道有没有弄坏别的地方。你不是要变成程序员,是要能审核 AI 的交付物。
这两个目标需要的能力完全不同。变成程序员要会写;审核交付物只需要三件事:
AI 能帮你写,但你必须能问出「这行在干什么」和「它假设了什么」。
后半句更重要。AI 写代码的时候会顺手补上它认为「应该有」的代码——校验、异常处理、边界情况,但这些都是它猜的,不是你要求的。它猜的东西看起来很专业,但可能和你的实际情况完全不符。
拿到一份 AI 生成的代码,按这四步走。顺序不能换,因为后一步依赖前一步的结论。
不看代码,先用大白话复述一遍这个功能是干什么的。如果你说不出来——那不是代码的问题,是需求本身你还没搞清。这种情况下继续看代码是浪费时间:你需要先回去把需求问清楚。
这一步是四步里最容易被跳过、也最不能跳的一步。AI 之所以能生成看起来很像样但实际不对的代码,根因常常就在这里——它按自己的理解补了一个需求。
看 package.json 的 scripts 字段(npm run dev 到底执行了什么)、main 字段(进程从哪个文件开始)、路由文件(挂了什么接口)。
为什么这一步排第二?因为它是「从哪儿开始读」的答案。找不到入口就无从读起,只能整盘grep,那是最慢的方式。
不要面面俱到。只挑一个——通常是那个「页面刷新就挂」或者「金额算错」的功能。从入口开始,一层一层往下追:页面 → 前端调用 → 后端路由 → 业务计算 → 查表→ 返回。
追完这一条线,你对这个项目的理解就从「听说它能跑」变成「我知道它是怎么跑的」。一条线胜过十处泛读——因为一条完整的线能让你发现「哪一环是猜的」。
① 这行为什么要这么写?② 如果数据是空的会怎样?③ 出错了会发生什么——是崩,还是悄悄返回错的东西?
第③ 问是四个里最重要的:「崩掉」你能立刻发现,「悄悄返回错的东西」你可能一个月后才在客户对账时发现。
这三个问题不需要你会写代码,只需要你把问题提出来。左边是问题,右边是它在测什么,最后一列是你项目里的真实教训。
| 问题 | 它在测什么 | 你项目里的对应教训 |
|---|---|---|
| 「如果输入是空的 / 错的会怎样?」 | 有没有校验。AI 很爱写校验,但它校验的是「类型对不对」,不一定是「业务上合不合法」 | 生产 400 单里 97% 无尺寸,引擎算不出价。因为「没尺寸」是个类型完全合法但业务上无效的数据,校验代码挡不住它,只能靠业务规则挡 |
| 「这里为什么会崩 / 丢数据?」 | 有没有兜底。崩了你能看到,丢了你看不到——后者危险得多 | collections 那个 JSON blob 写坏整库。一个字段存了整个 JSON 对象,一次写坏就是整条记录报废,而数据库自己不一定拦得住 |
| 「这段代码删了会怎样?」 | 是不是重复代码 / 死代码。如果删了没影响,那它就不该存在——留着只会让人误以为它有用 | ——这一条在你项目里目前没有反面案例,说明你的代码还算干净。但要养成问的习惯,因为 AI 生成的东西里冗余代码非常多(它会把同一件事写两遍,两遍都「对」) |
看到下面任何一个,先别急着接受这份交付。这一节是整篇最实用的——它不需要任何知识,看到形式上的异常就能判断。
| # | 危险信号 | 它意味着什么 |
|---|---|---|
| 1 | 单个字段里塞了整整一个 JSON 对象 | 为了快、为了省事。后果是没法查、没法约束、没法部分更新。你项目里的 collections就是这个问题——JSON blob 写坏整库就是从这里来的 |
| 2 | 注释写着「TODO」「暂时」「先这样」「后续优化」 | AI 在告诉你它知道这里不完整。它没做,但也没说「这我没做」。这个标记就是你唯一的机会去追问 |
| 3 | try...catch 里什么都不做(吞异常) | 出了错没人知道。比崩掉更糟——崩掉你会去查,吞掉你就永远不知道发生过 |
| 4 | 拼 SQL 字符串 | 直接是 SQL 注入漏洞。见第 04 节表格里的对比。这一条要立刻改,没有商量余地 |
| 5 | 密码 / token 硬编码在前端代码里 | 任何能看到前端 JS 的人都能拿到。浏览器里的东西等于公开的 |
| 6 | 循环里查数据库(N+1 查询) | 现在数据少看不出问题,10 万条就卡死。详见深入版第 06 节 |
| 7 | 没有分页的列表接口 | 数据量一上来就慢,严重时会拖垮整个服务。而且这种问题在你调试时(数据少)永远看不出来 |
| 8 | 状态用魔法数字 ( status: 1 / status: 2,不写含义) | 半年后你自己也不记得 2 是什么。要么写枚举,要么写注释 |
| 9 | 一个测试也没有 | 你永远不会知道改动有没有弄坏别的地方。你项目现状:全盘 0 个测试文件(只有 packages/engine/test/run.ts 用 node:assert 手写了 6 个型号断言) |
| 10 | 依赖锁死老版本 | 老版本可能有已知漏洞。你项目里 Vite 有 5/6/8 三个版本、Tailwind 有 3 和 4——跨度大是正常的,但要知道每个版本意味着什么 |
下面这段模板直接复制就能用。它专门解决「AI 讲得太术语化,你听不懂」这个问题——格式本身就是约束。
我要读懂这段代码,请按下面格式回答,不要堆术语:
这段代码里如果有个地方写错了,最可能错在哪?帮我设计一个能暴露这个错误的测试。
拿到任何一份交付,按这个清单跑一遍。不需要会写代码,只需要会敲命令、看返回结果。
照着 README 或 package.json 的 scripts 起服务。能起来是最低门槛,不是验收通过。起不来就直接打回,不往下走。
这是验证「它真在干活」的最快办法。比如把一个数量改成 1、把价格改个数字、换个输入值。如果改了什么都没变,说明这段代码可能压根没被执行到(或者它写死了)。
对应第 04 节的第一个问题。看它是崩掉、返回空,还是返回一堆没意义的东西。返回 200 但内容为空,是最难发现的一种问题。
把日志级别调到最详细,跑一遍完整流程。日志告诉你「它内部到底走了哪条分支」——这比读代码快得多,也可靠得多。
按第 03 节第 2 步找到入口,只读主流程那一段,不用读全。目的是确认「它做的事和你以为的是同一件」。
接口返回「成功」不代表数据是对的。必须做两件事:看字节数 + 采样内容。
你项目里的真实例子:主站 84 个 URL 返回 200,但每个只有 3120 字节空壳——200 全对,内容全错。再比如 LumenX 200但整站 401,爬虫拿到的是登录页。所以任何「验证一下」都必须落到「打开返回内容,确认里面有我要的东西」这一步。
生产上的东西改错了不能靠「再让AI 改回来」——因为你不确定它当初是怎么写的、也不确定有没有别的依赖。顺序永远是:先只读看状态 → 备份 → 再改。
反向的例子:直接动生产数据的操作(比如 db:seed、改表结构),先在本地跑一遍。你项目里 db:seed 是会覆盖已有数据的,在生产随手跑一下,数据就没了。
AI 给你装一个你没听过的包时它不会提醒你这是个大风险。你只需要做一件事:打开它的说明页看「它是干什么的」——看不懂就是不能装。
为什么这条重要:装一个包就等于把一段别人写的代码放进你的系统,而它现在能读你的数据、能往外发请求。你项目里的依赖结构其实是很健康的:0 个 Redis、0 个消息队列、0 个 WebSocket、0 个 MySQL/PostgreSQL——攻击面小、能读懂。新增一个中间件之前,先想清楚能不能用现有方案解决。
我先说清定位:我不是要变成程序员,我是要能审核 AI 的交付物。这两件事需要的能力完全不同——审核只需要判断它对不对、判断它值不值得信、以及出事知道往哪查。
我的方法叫四步验收法。第一步最关键:用大白话复述这个功能要干什么。如果我说不出来,说明需求本身我还没搞清,这时候继续看代码是浪费时间,得先回去把需求问清楚。第二步找入口:看 package.json 的 scripts 和 main、看路由文件挂了什么。第三步追一条线:只挑一个最关键的功能,从入口一路追到数据库——一条完整的线胜过十处泛读,因为它能让你发现「哪一环是猜的」。第四步问三个问题。
三个必问的问题,我给出真实教训:①「如果输入是空的或错的会怎样」——测的是有没有校验。我的教训是生产 400 单里 97% 无尺寸,引擎算不出价:因为「无尺寸」是类型上完全合法、业务上完全无效的数据,校验代码挡不住它,只能靠业务规则挡。②「这里为什么会崩或丢数据」——测的是有没有兜底。崩了你能发现,丢了你发现不了。我的教训是collections 那个 JSON blob 写坏整库。③「这段代码删了会怎样」——测的是有没有重复/死代码。
最快的工具是一张危险信号清单。看到这些形式上的特征就警惕:单个字段塞整个 JSON 对象、注释写着「TODO / 暂时 / 先这样」、try-catch 里什么都不做、拼 SQL 字符串、密码硬编码在前端、循环里查数据库、列表接口没分页、状态用魔法数字、一个测试也没有、依赖锁死老版本。这十条不需要会写代码,看到形式异常就能判断。我的原则是:不是阻止这些代码上线,而是知道这里要问一句。
让 AI 讲代码这件事,我用的是固定模板:要它按六项回答——整体作用一句话、入口和调用链、逐段解释关键行、依赖哪些字段和外部服务、上游为空会怎样、有哪些隐含假设。并且一定要追加一问:「这段代码如果有个地方写错了,最可能错在哪?帮我设计一个能暴露这个错误的测试。」这一问最值钱:如果它答不上来,说明它自己也没理解这段代码,那我就知道不能上线;如果答得上来,我顺手就拿到了一个测试用例。
落到操作上,我有一套 15 分钟的最小可行验收:跑起来 → 改一个参数看变化 → 故意传空值 → 看日志 → 找入口读一遍。其中「改一个参数看变化」最关键:如果改了什么都没变,说明这段代码可能压根没被执行到,或者它写死了。
我自己守三条红线:①看数据不看日志——接口返回成功不代表数据对,必须看字节数 + 采样内容。这是我真实踩过的:主站 84 个 URL 全返回 200,但每个只有 3120 字节空壳,还有 LumenX 返回 200 但整站 401、爬虫拿到的是登录页。②改生产系统前先只读诊断 + 备份,顺序永远是先看状态、再备份、再改——因为改错了不能靠「让 AI 改回来」。③不认识的依赖先查它是干什么的,别默认可信,装一个包等于把别人写的代码放进我的系统。
最后我要诚实说一个我自己的短板:我全盘零个测试文件——*.test.* 和 *.spec.* 一个都没有。唯一的例外是 delimao-system 的 packages/engine/test/run.ts,用 node:assert 手写了 6 个型号的断言。这意味着我能确认「代码能跑」,但不能确认「改动没有弄坏别的地方」。这是我下一步要补的第一件事,方向很明确:从 engine 里那个纯报价计算函数开始,一个函数一个函数补,而不是等学完测试框架再开始。
上面是速查版,只讲「怎么问、怎么查、怎么判」。AI 生成代码为什么会补全它认为该有的代码、怎么按项目类型定位入口、怎么追一条调用链的六站、依赖三层追法、N+1 怎么自己发现、异常处理三种坏味道、SQL 注入怎么自查、依赖审计多久做一次、测试怎么从 0 到 1都在深入版: