首页 › Vibe coding 项目实战专栏 › 第 1 章:什么是 Vibe Coding? › 第 11 章:代码质量与安全

第 11 章:代码质量与安全

🎯 本章目标:​

Vibe Coding 追求速度,但绝不以牺牲质量为代价。​

本章将教你如何识别 AI 埋下的"雷",建立自动化的安全防线,从一名"生成者"晋升为"把关者"。​

11.1 AI 生成代码的常见问题 (The AI Smells)​

AI 写出的代码往往"看起来很美",但跑起来可能全是坑。你需要警惕以下特征(我们称之为 AI Smells):​

幻觉依赖 (Hallucinated Dependencies)​

AI 经常会引入一些根本不存在,或者版本早已过时的库。​

•

现象:import { useSuperPower } from 'react-magic'​

•

对策:​

a.

查文件:检查 package.json,确认该包是否在 dependencies 中。​

b.

查终端:运行 npm list react-magic 看看是否安装。​

c.

问 AI:"这个库真的存在吗?还是你编的?"​

隐蔽的逻辑错误 (Subtle Logic Bugs)​

AI 写代码很快,但不一定细心。​

•

现象:​

◦

Off-by-one:循环多跑了一次或少跑了一次 (i <= length vs i < length)。​

◦

静默失败:try-catch 捕获了错误但什么都没做(Empty Catch Block),导致 Bug 难以定位。​

•

对策:重点 Review 循环边界和异常处理逻辑。​

过度设计 (Over-Engineering)​

有时候你只需要一个简单的 if-else,AI 却给你整了一套策略模式。​

•

现象:当你发现生成的代码里有大量的 Factory, Abstract, Proxy,但业务逻辑却很简单时。​

•

对策:在 Prompt 中加上约束:Keep it simple (KISS principle). Avoid unnecessary abstractions.​

上下文丢失 (Context Amnesia)​

当对话轮次过多,AI 可能会忘记你一开始设定的 typescript: true 或 tailwind: true 规则。​

•

现象:突然开始写普通的 CSS,或者变量变成了 any 类型。​

•

对策:及时 New Chat,或者在 Custom Instructions 里把核心规则焊死。​

​

​

11.2 安全漏洞识别与防范​

AI 训练数据里包含了大量优秀的开源代码,也包含了大量有漏洞的烂代码。它不知道哪个是安全的。​

密钥泄露 (Hardcoded Secrets)​

这是最致命的。AI 可能会为了方便,直接把 Key 写在代码里。​

•

❌ const API_KEY = "sk-123456...";​

•

✅ 必须强制 AI 使用环境变量:const API_KEY = process.env.NEXT_PUBLIC_API_KEY;​

•

工具推荐:在 git commit 前使用 gitleaks 或 Husky 钩子扫描代码,防止密钥入库。​

数据隐私 (Data Privacy)​

不要把公司的核心机密或用户的 PII (个人敏感信息) 发送给 AI。​

•

❌ "这是我们要分析的真实客户名单:[张三, 138xxxx...]"​

•

✅ "这是一份脱敏后的测试数据,包含 name 和 phone 字段..."​

XSS 注入 (Cross-Site Scripting)​

AI 在处理用户输入时,有时会直接使用 dangerouslySetInnerHTML 或者不安全的拼接。​

•

审查点:搜索代码里的 innerHTML 关键字。如果不是必须,严禁使用。​

SQL 注入​

虽然现代 ORM (如 Prisma) 已经防范了大部分注入,但如果 AI 生成了原生的 queryRaw,必须要警惕。​

•

❌ prisma.$queryRaw\SELECT * FROM User WHERE name = '${name}'``​

•

✅ 使用参数化查询。​

​

​

11.3 代码审查 Checklist (人工防线)​

在点击 Merge 之前,请对照这份清单过一遍:​

​

#CuXwTaGAsDAT text { white-space: pre; }#CuXwTaGAsDAT .fb0 { stroke: var(--N900-FG, 0.15); }#CuXwTaGAsDAT .fb0 {stroke: rgba(31,35,41, 0.15)}#CuXwTaGAsDAT .fx0 { stroke-miterlimit: 10; }#CuXwTaGAsDAT .fx0 {stroke-miterlimit: 10}#CuXwTaGAsDAT .fa1 { fill: rgb(187, 191, 196); }#CuXwTaGAsDAT .fa1 {fill: rgb(187, 191, 196)}#CuXwTaGAsDAT .fx1 { font: 400 12px MonospacedNumber, LarkHackSafariFont, LarkEmojiFont, LarkChineseQuote, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Tahoma, "PingFang SC", "Microsoft Yahei", Arial, "Hiragino Sans GB", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; }#CuXwTaGAsDAT .fx1 {font-style: normal}#CuXwTaGAsDAT .fx2 { text-anchor: middle; }#CuXwTaGAsDAT .fx2 {text-anchor: middle}#CuXwTaGAsDAT .fx3 { dominant-baseline: central; }#CuXwTaGAsDAT .fx3 {dominant-baseline: central}#CuXwTaGAsDAT .fx4 { stroke: none; }#CuXwTaGAsDAT .fx4 {stroke: none}#CuXwTaGAsDAT .fc0 { fill: var(--text-caption); }#CuXwTaGAsDAT .fc0 {fill: #646a73}#CuXwTaGAsDAT .fa2 { fill: var(--text-link-disabled); }#CuXwTaGAsDAT .fa2 {fill: rgb(187, 191, 196)}#CuXwTaGAsDAT .fx5 { font: 400 16px MonospacedNumber, LarkHackSafariFont, LarkEmojiFont, LarkChineseQuote, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Tahoma, "PingFang SC", "Microsoft Yahei", Arial, "Hiragino Sans GB", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; }#CuXwTaGAsDAT .fx5 {font-style: normal}#CuXwTaGAsDAT .fx6 { text-anchor: start; }#CuXwTaGAsDAT .fx6 {text-anchor: start}#CuXwTaGAsDAT .fx7 { dominant-baseline: alphabetic; }#CuXwTaGAsDAT .fx7 {dominant-baseline: alphabetic}#CuXwTaGAsDAT .fc1 { fill: rgb(31, 35, 40); }#CuXwTaGAsDAT .fc1 {fill: rgb(31, 35, 40)}#CuXwTaGAsDAT .fx8 { font: 400 13px LarkHackSafariFont, LarkEmojiFont, ui-monospace, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, "Noto Sans", Tahoma, "Nimbus Sans L", Arial, "Liberation Sans", "PingFang SC", "Hiragino Sans GB", "Noto Sans CJK SC", "Source Han Sans SC", "Source Han Sans CN", "Microsoft YaHei", "Wenquanyi Micro Hei", "WenQuanYi Zen Hei", STHeiti, SimHei, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji", system-ui; }#CuXwTaGAsDAT .fx8 {font-style: normal}#CuXwTaGAsDAT .fb1 { fill: var(--N900-FG, 0.15); }#CuXwTaGAsDAT .fb1 {fill: rgba(31,35,41, 0.15)}#CuXwTaGAsDAT .fb2 { fill: rgb(209, 217, 224); }#CuXwTaGAsDAT .fb2 {fill: rgb(209, 217, 224)}#CuXwTaGAsDAT .fa3 { fill: var(--bg-body); }#CuXwTaGAsDAT .fa3 {fill: #fff}ABC123456维度功能安全性能风格依赖检查项业务逻辑敏感信息循环/递归类型定义包体积通过标准代码真的实现了我想要的功能吗?还是只是"看起来像"?没有任何 Key 或密码硬编码?有没有无意义的 嵌套循环?是否消灭了所有的 ?是否引入了为了一个小功能而引入了巨大的库(如 moment.js)?O(n^2)any

​

11.4 测试覆盖率与自动化测试 (机器防线)​

人眼总会看走眼,机器不会。在 Vibe Coding 时代,测试代码其实是最好写的。​

策略:Test-Driven Vibe (TDV)​

先生成测试:让 AI 理解业务后,先生成测试文件 *.test.ts。​

验证实现:让 AI 生成业务代码,运行测试。​

补充边界:专门在此 Prompt:"请针对这个函数,生成 3 个极端的边界测试用例(如空数组、超大数值、非法字符)。"​

实战指令​

​

"我已经写好了 PaymentService.ts。 请为它编写单元测试:​

Mock 掉 Stripe 的支付接口。​

测试支付成功、余额不足、网络超时三种情况。​

确保测试覆盖率达到 90% 以上。"​

​

只要测试是绿的,你就可以放心地把代码推送到生产环境。​

​

​

📝 本章小结​

Vibe Coding 不是让大脑休息,而是让大脑升级。你从"拼写员"变成了"审计员"。​

•

警惕幻觉:不要盲信 import。​

•

守住安全:严查 Key 泄露和注入。​

•

依赖测试:既然 AI 写测试这么快,就让测试覆盖率卷到 100%。​

​