FDE·技术基础
基础目录
速查版 · 一屏看懂
①请求旅程 ②读懂monorepo ③数据库存东西 ④并发锁事务 ⑤前端为什么能跑 ⑥读懂AI代码
深入版 · 原理到能排障
①请求旅程 ②monorepo ③数据库 ④并发事务 ⑤前端渲染 ⑥读懂AI代码

基础 19 · 怎么读懂 AI 写的代码

速查版:一屏看懂怎么审核 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 个项目、几十个后端包,一个人跑完,没有这些不可能。
真实风险:讲不出为什么这样写,出问题只能从头猜。

全盘最刺眼的一个数字:测试 0 个。*.test.* 和 *.spec.* 全盘 0 个命中。唯一的例外是 delimao-system 的 packages/engine/test/run.ts——用 node:assert 手写了 6 个型号的断言。这是 AI 生成代码最大的结构性漏洞:代码能跑,但没人知道它是不是对。改动之后没人知道有没有弄坏别的地方。
01

先把定位说清楚

你不是要变成程序员,是要能审核 AI 的交付物。

这两个目标需要的能力完全不同。变成程序员要会写;审核交付物只需要三件事:

要会
判断它对不对
不是看代码长得漂不漂亮,是问几个能暴露问题的问题(第 03、04 节给你现成的问题清单)。这一节不需要你会写代码。
要会
判断它值不值得信
看到几个特定信号就该警惕(危险信号清单)。这一节也不需要你会写代码,只需要会看。
要会
最小可行验收——15 分钟跑一遍,能立刻判断「这活儿是不是交付到位」。这一节你只需要会点命令、会看返回结果。
为什么这一篇比前面 17 篇都重要?因为前面那些是「知识」——知道 DNS 是什么、React 怎么渲染。但 AI 写代码这件事,把「学了没用」和「学了有用」的距离拉到了最短:你不需要会写代码,你只需要能问出「这行在干什么」和「它假设了什么」,就能拦住 90% 的问题。
说清一件容易被误解的事:用 AI 开发不等于「验收结果就行」。验收结果(页面能打开、接口返回了数据)只能证明「它跑起来了」,不能证明「它是对的」。这两个中间有一条很宽的缝,全篇讲的就是怎么把这条缝补上。
02

一句话

AI 能帮你写,但你必须能问出「这行在干什么」和「它假设了什么」。

后半句更重要。AI 写代码的时候会顺手补上它认为「应该有」的代码——校验、异常处理、边界情况,但这些都是它猜的,不是你要求的。它猜的东西看起来很专业,但可能和你的实际情况完全不符。

你项目里最典型的例子:生产 400 单里有 97% 是「无尺寸」的。那套处理无尺寸订单的代码,是 AI 补上去的——它补得很完整(有默认值、有提示、有兜底),但它没法替你决定「无尺寸的订单该怎么报价」,因为那是个业务决策,不是技术决策。所以结果就是:代码跑得好好的,业务照样算不出价。
03

四步验收法(核心方法论)

拿到一份 AI 生成的代码,按这四步走。顺序不能换,因为后一步依赖前一步的结论。

1

说清它要干什么(用你自己的话)

不看代码,先用大白话复述一遍这个功能是干什么的。如果你说不出来——那不是代码的问题,是需求本身你还没搞清。这种情况下继续看代码是浪费时间:你需要先回去把需求问清楚。

这一步是四步里最容易被跳过、也最不能跳的一步。AI 之所以能生成看起来很像样但实际不对的代码,根因常常就在这里——它按自己的理解补了一个需求。

2

找入口:项目从哪启动

看 package.json 的 scripts 字段(npm run dev 到底执行了什么)、main 字段(进程从哪个文件开始)、路由文件(挂了什么接口)。

为什么这一步排第二?因为它是「从哪儿开始读」的答案。找不到入口就无从读起,只能整盘grep,那是最慢的方式。

3

追一条线:挑一个最关键的功能,从入口一路追到数据库

不要面面俱到。只挑一个——通常是那个「页面刷新就挂」或者「金额算错」的功能。从入口开始,一层一层往下追:页面 → 前端调用 → 后端路由 → 业务计算 → 查表→ 返回。

追完这一条线,你对这个项目的理解就从「听说它能跑」变成「我知道它是怎么跑的」。一条线胜过十处泛读——因为一条完整的线能让你发现「哪一环是猜的」。

4

问三个问题(下一节有完整版)

① 这行为什么要这么写?② 如果数据是空的会怎样?③ 出错了会发生什么——是崩,还是悄悄返回错的东西?

第③ 问是四个里最重要的:「崩掉」你能立刻发现,「悄悄返回错的东西」你可能一个月后才在客户对账时发现。

整套方法的成本:第1 步 5 分钟(复述一遍)、第 2 步 5 分钟(看两个文件)、第 3 步 30~60 分钟(追一条线)、第 4 步 20 分钟(问三个问题 + 让 AI 回答)。第一个小时之后,一个功能你就真的懂了。
04

三个必问的问题

这三个问题不需要你会写代码,只需要你把问题提出来。左边是问题,右边是它在测什么,最后一列是你项目里的真实教训。

问题它在测什么你项目里的对应教训
「如果输入是空的 / 错的会怎样?」有没有校验。AI 很爱写校验,但它校验的是「类型对不对」,不一定是「业务上合不合法」生产 400 单里 97% 无尺寸,引擎算不出价。因为「没尺寸」是个类型完全合法但业务上无效的数据,校验代码挡不住它,只能靠业务规则挡
「这里为什么会崩 / 丢数据?」有没有兜底。崩了你能看到,丢了你看不到——后者危险得多collections 那个 JSON blob 写坏整库。一个字段存了整个 JSON 对象,一次写坏就是整条记录报废,而数据库自己不一定拦得住
「这段代码删了会怎样?」是不是重复代码 / 死代码。如果删了没影响,那它就不该存在——留着只会让人误以为它有用——这一条在你项目里目前没有反面案例,说明你的代码还算干净。但要养成问的习惯,因为 AI 生成的东西里冗余代码非常多(它会把同一件事写两遍,两遍都「对」)
为什么这三个问题有效?因为它们都不需要读懂具体代码,只需要把问题提出来。而 AI 回答这些问题时,会暴露出它当时假设了什么。它假设的东西,往往就是它没跟你确认的东西。
用法提示:把这三个问题一次性全问,不要挤牙膏式地问。挤牙膏的问题是每次回答都建立在上一个回答之上,你会被它带跑;一次性问,它会一次性把假设摊开,你能横向比较。
05

危险信号清单(最快用的工具)

看到下面任何一个,先别急着接受这份交付。这一节是整篇最实用的——它不需要任何知识,看到形式上的异常就能判断。

#危险信号它意味着什么
1单个字段里塞了整整一个 JSON 对象为了快、为了省事。后果是没法查、没法约束、没法部分更新。你项目里的 collections就是这个问题——JSON blob 写坏整库就是从这里来的
2注释写着「TODO」「暂时」「先这样」「后续优化」AI 在告诉你它知道这里不完整。它没做,但也没说「这我没做」。这个标记就是你唯一的机会去追问
3try...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——跨度大是正常的,但要知道每个版本意味着什么
这份清单的正确用法:不是让你阻止这些代码上线,而是让你知道「这里要问一句」。看到第 3 条,你就问「这里出错会怎样」;看到第 6 条,你就问「数据量大了会怎样」;看到第 9 条,你就知道这次交付是「能跑」而不是「是对的」。
有个正面参照值得说:你项目的依赖里没有 Redis、没有消息队列、没有 WebSocket、没有 MySQL/PostgreSQL、没有图表库、没有 antd。这不是「功能不够」——是「依赖少 = 攻击面小 = 能读懂」。每多一个中间件,你就多一个「它到底做了什么、我怎么知道它没出错」的盲区。
06

怎么让 AI 把代码讲给你听(可直接复制)

下面这段模板直接复制就能用。它专门解决「AI 讲得太术语化,你听不懂」这个问题——格式本身就是约束。

📋 提问模板(复制这段,把最后的占位换成你的内容)

我要读懂这段代码,请按下面格式回答,不要堆术语:

  1. 这段代码的整体作用(一句话)
  2. 入口在哪、调用链经过哪些文件(按顺序列文件名)
  3. 逐段解释关键几行的意思(每行说明它在干什么、为什么需要)
  4. 它依赖哪些字段 / 表 / 外部服务(全部列出)
  5. 如果上游数据为空或异常,会发生什么(崩掉 / 返回错的东西 / 返回空)
  6. 有哪些隐含假设(比如「假设时间格式是 xxx」「假设金额单位是元」)
📋 追加一问(这句最值钱,务必一起问)

这段代码里如果有个地方写错了,最可能错在哪?帮我设计一个能暴露这个错误的测试。

最后一问为什么最值钱?因为它把「审代码」变成了「让 AI 交一份作业」。如果它连「哪里可能错」都答不上来,说明它自己也没理解这段代码——那你就知道这段代码不能上线。如果它答得上来,你就顺手拿到了一个测试用例,而测试正是你全盘最缺的东西。
一个提醒:AI 讲代码时也会编。它会用非常自信的语气说「这里会校验空值」,哪怕代码里根本没有。所以第 ① 步「自己复述一遍」不能省——你要用它自己的说法去核对代码,看是不是真的一致。
07

最小可行验收(15 分钟能做完)

拿到任何一份交付,按这个清单跑一遍。不需要会写代码,只需要会敲命令、看返回结果。

1

跑起来(约 5 分钟)

照着 README 或 package.json 的 scripts 起服务。能起来是最低门槛,不是验收通过。起不来就直接打回,不往下走。

2

改一个参数,看变化(约 3 分钟)

这是验证「它真在干活」的最快办法。比如把一个数量改成 1、把价格改个数字、换个输入值。如果改了什么都没变,说明这段代码可能压根没被执行到(或者它写死了)。

3

故意传空值 / 错值(约 3 分钟)

对应第 04 节的第一个问题。看它是崩掉、返回空,还是返回一堆没意义的东西。返回 200 但内容为空,是最难发现的一种问题。

4

看日志(约 2 分钟)

把日志级别调到最详细,跑一遍完整流程。日志告诉你「它内部到底走了哪条分支」——这比读代码快得多,也可靠得多。

5

找入口文件,读一遍(约 2 分钟)

按第 03 节第 2 步找到入口,只读主流程那一段,不用读全。目的是确认「它做的事和你以为的是同一件」。

这套动作真正的价值:它把「我觉得应该没问题」变成「我验证过它按我以为的方式在跑」。第2 步和第 3 步尤其关键——它们能抓到「代码存在但从没被执行」和「错误被静默吞掉」这两类最隐蔽的问题。
08

你自己必须守的三条

红线 1 · 看数据不看日志

接口返回「成功」不代表数据是对的。必须做两件事:看字节数 + 采样内容。
你项目里的真实例子:主站 84 个 URL 返回 200,但每个只有 3120 字节空壳——200 全对,内容全错。再比如 LumenX 200但整站 401,爬虫拿到的是登录页。所以任何「验证一下」都必须落到「打开返回内容,确认里面有我要的东西」这一步。

红线 2 · 改生产系统前先只读诊断 + 备份

生产上的东西改错了不能靠「再让AI 改回来」——因为你不确定它当初是怎么写的、也不确定有没有别的依赖。顺序永远是:先只读看状态 → 备份 → 再改。
反向的例子:直接动生产数据的操作(比如 db:seed、改表结构),先在本地跑一遍。你项目里 db:seed 是会覆盖已有数据的,在生产随手跑一下,数据就没了。

红线 3 · 不认识的依赖先查它是干什么的,别默认可信

AI 给你装一个你没听过的包时它不会提醒你这是个大风险。你只需要做一件事:打开它的说明页看「它是干什么的」——看不懂就是不能装。
为什么这条重要:装一个包就等于把一段别人写的代码放进你的系统,而它现在能读你的数据、能往外发请求。你项目里的依赖结构其实是很健康的:0 个 Redis、0 个消息队列、0 个 WebSocket、0 个 MySQL/PostgreSQL——攻击面小、能读懂。新增一个中间件之前,先想清楚能不能用现有方案解决。

✓

自查清单

  • ✓我清楚定位:我要审核 AI 的交付物,不是变成程序员
  • ✓我知道真实风险不是「跑不起来」,是「跑起来了但不知道对不对」
  • ✓我能背出四步验收法:说清作用 → 找入口 → 追一条线 → 问三个问题
  • ✓我知道第一步「说不出来就说明需求没搞清」,而且不能跳过
  • ✓我能提那三个必问问题,并知道第③ 问(崩还是静默返回错东西)最重要
  • ✓我知道生产 400 单 97% 无尺寸这个教训对应「类型合法但业务无效」
  • ✓我知道collections 的 JSON blob 写坏整库这个教训对应「崩掉 vs 丢数据」
  • ✓我能认出危险信号清单里的十条,尤其:吞异常、拼 SQL、循环里查库、没有测试
  • ✓我知道看到「TODO / 暂时 / 先这样」就是在给我一个追问的机会
  • ✓我能复制那段提问模板,并且会追加「最可能错在哪 + 帮我设计测试」那一问
  • ✓我能在 15 分钟内跑完最小可行验收,尤其会「改一个参数看变化」和「故意传空值」
  • ✓我守三条红线:看数据不看日志 / 改生产前先只读诊断+备份 / 不认识的依赖先查再装
  • ✓我诚实面对:全盘 0 个测试文件这件事,是我下一步要补的
面试怎么答

题目:你用 AI 开发,怎么保证质量?

我先说清定位:我不是要变成程序员,我是要能审核 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都在深入版:

深入版 19 · 读懂 AI 代码深入 →