合并AI代码前,先核实依赖与API
文章提醒,AI代码即使格式规范、正常路径测试通过,也可能包含看似合理的错误。摘录给出依赖与API核验方法,包括检查包发布者、对照所用版本文档,以及重视安装和类型检查错误。
AI 深度解读
AI 生成的代码最容易让人放松警惕的地方,是它看起来已经写对了:命名整齐、注释完整,正常流程一跑就通。作者提醒,合并前真正要找的是那些语法正确、逻辑顺滑,却把权限、异常或输入前提想错的代码。
这份检查清单围绕 6 个问题展开,适合写 Next.js、Node API 和后台系统的程序员。它没有证明 AI 比人更容易犯哪些错,但提供了一套实用的审查顺序:先核对代码依赖的事实,再检查业务边界,最后判断测试有没有跟着实现一起误解需求。
能运行的代码,也可能用错 API 和版本
作者首先检查每个依赖和方法是否真实存在。模型可能拼出一个听起来合理的包名,也可能引用旧版本中存在、当前版本已经移除的接口。
原文举了两个反例:从 time-utils-pro 导入 parseDuration,以及调用 Array.prototype.groupBy(items, 'type')。它们是待核验的示例,不能直接当成推荐写法。
核对时,要去包注册表看发布者和更新时间,再对照 package.json 中实际使用版本的文档。安装失败或类型检查报错时,应该追查错误,不能因为生成代码写得笃定,就默认环境出了问题。
这里还有依赖安全风险:模型编造的包名,如果后来被别人注册,可能成为恶意依赖的入口。不能为了消除一条 import 报错,就顺手安装一个来历不明的包。
版本问题也一样。作者以 Next.js 15 为例,提醒检查异步参数访问:页面中的 params、searchParams,以及 route handler 中的 params,需要关注 Promise 和 await。原文把页面与路由处理器的表述合在了一起,实际审查仍要落实到具体接口。
作者还提醒检查:框架已有更合适的服务端取数方式时,是否仍机械地在 useEffect 中请求数据;配置文件里是否出现已经废弃、被忽略或直接遭拒的选项。提示词写明精确版本能减少偏差,但不能替代核验。
登录成功,不代表能操作这条数据
作者认为,生成 API 路由时,一个常见的严重遗漏是只检查登录状态,没有检查数据归属。这是他的观察,原文没有提供发生比例。
下面的删除接口能拦住未登录请求,却没有阻止其他登录用户删除别人的发票:
export async function DELETE(req: Request, { params }: { params: Promise<{ id: string }> }) {
const session = await getSession();
if (!session) return new Response('Unauthorized', { status: 401 });
const { id } = await params;
await Invoice.findByIdAndDelete(id); // 任何登录用户都能删除任意发票
return new Response(null, { status: 204 });
}
要盯住的是 Invoice.findByIdAndDelete(id):查询只使用请求提供的 ID,没有把当前用户与目标记录关联起来。
身份认证回答「你是谁」,授权回答「你能操作什么」。这两个问题不能由一次 session 检查同时解决。作者给出的修正,是直接把归属条件放进删除查询:
const deleted = await Invoice.findOneAndDelete({ _id: id, userId: session.userId });
if (!deleted) return new Response('Not found', { status: 404 });
关键是查询中的 userId: session.userId。对于每个接收 ID 的接口,都可以追问一句:用户 A 传入用户 B 的 ID,究竟是哪条条件拦住了它?
输入边界则是另一道关。原文用 User.findOne({ email: body.email, password: body.password }) 举例:如果 JSON 请求体未经验证,攻击者传入 { "email": "a@b.com", "password": { "$ne": null } },原本预期的密码字符串就变成了查询操作符,可能在不知道密码时匹配记录。
作者建议先用 Zod 等 schema 工具验证输入结构,同时不要在查询中直接比较明文密码,应使用密码哈希校验。
TypeScript 的类型约束不能替代运行时输入验证。
还要试空字符串、空数组、null、错误类型,以及重复请求和并发请求。示例输入能够跑通,只能证明那个示例被照顾到了。
异常和测试,会不会一起制造假成功
作者对每个 catch 都问同一个问题:发生失败后,谁会知道?
最明显的错误,是数据库写入失败后仍返回 { success: true }。更隐蔽的版本,是记录日志后继续执行,用默认值掩盖请求失败,或者让「记录不存在」和「数据库不可用」都返回 null。
这些代码不一定立刻报错,却会让调用方作出错误决定。审查时要沿着返回值继续看:页面是否会显示保存成功,后续逻辑是否还会继续运行,失败是否有机会被处理。
测试全绿也不能自动消除这个疑问。作者提醒,同一个工具生成实现和测试时,可能把同一个误解复制两遍。测试验证了实现内部的一致性,却没有验证需求。
对于发票删除接口,关键测试应该来自业务规则:用户 B 不能删除用户 A 的发票。只验证合法用户能删除自己的发票,很容易漏掉权限缺口。
最后还要看 diff 的范围。日期格式化需求是否带来了新依赖、配置文件和工具类?是否修改了无关文件?是否用 120 行代码完成标准库两行就能做的事?这些新增内容都会进入后续维护范围,也增加审查负担。
我的判断
这份清单最有价值的地方,是把审查从「代码像不像对的」转向「它依赖的前提是否成立」。权限、外部输入和失败反馈,确实适合优先检查,因为正常流程通常不会主动暴露这些问题。
但它是一份经验清单。作者没有提供缺陷统计、对照实验,也没有量化采用清单后的收益,不能据此判断各类错误的发生频率。
部分建议也有适用前提。userId 适合个人归属的数据模型,多租户、共享资源或角色权限系统还需要其他授权条件;catch 也不必一律重新抛错,合理降级可以成立,前提是调用方能够区分降级结果与正常成功。
测试是否可靠,取决于断言有没有独立的需求依据。
人工编写同样可能照抄实现思路。把这份清单与类型检查、自动化测试结合起来,比单独依赖其中任何一种更稳妥。
能拿来做什么
- 给 Next.js 或 Node 的 PR 模板加入这 6 类检查项,并要求关键项附上依据,例如版本文档、授权查询条件或失败处理方式。
- 对接收资源 ID 的接口,优先补上跨用户访问测试;如果业务存在租户或角色,再按实际权限规则扩展断言。
- 让 AI 编码前先明确框架版本、输入 schema 和失败时的返回约定,审查时重点看这些约定是否真正进入实现。
- 合并前单独检查新增依赖和无关文件,每一项都应能对应到当前需求,避免小功能带来持续维护成本。