代码审查与验收标准
为什么 AI 生成的代码需要严格审查
AI 编程助手能显著提升开发效率,但这并不意味着可以不经审查就将其输出直接部署到生产环境。AI 生成的代码存在一类特有的问题:它在表面上看起来完全正确,但在细节上可能隐藏着严重缺陷。
与人类开发者的错误不同,AI 的错误往往不是因为"不懂",而是因为:
- 训练数据中存在过时的 API 用法
- 对业务上下文的理解不完整
- 过度泛化,忽略了特定场景下的边界条件
- 生成了看起来合理但实际不存在的函数或方法(幻觉)
作为开发者,你的角色从"编写代码"转变为"审查和把关代码质量"。这同样需要专业技能,只是技能的重心发生了转移。
AI 代码常见问题类型
1. 幻觉 API(Hallucinated APIs)
AI 有时会生成不存在的函数、方法或模块:
javascript
// AI 生成的代码(问题示例)
import { parseJWT, validateTokenExpiry } from 'jsonwebtoken';
// ❌ jsonwebtoken 没有 parseJWT 和 validateTokenExpiry 导出
// 正确的做法
import jwt from 'jsonwebtoken';
const decoded = jwt.verify(token, secret); // ✓ 实际存在的 API审查方法:对所有引入的函数和方法,快速查阅官方文档或在 IDE 中确认自动补全能找到。
2. 错误的边界条件处理
python
# AI 生成的代码(问题示例)
def get_user_by_id(user_id: int) -> User:
return db.query(User).filter(User.id == user_id).first()
# ❌ 没有处理 user_id 为负数、0 或超大值的情况
# ❌ 没有处理数据库连接失败的情况
# ❌ 返回 None 时调用方可能未预期
# 更健壮的版本
def get_user_by_id(user_id: int) -> Optional[User]:
if not isinstance(user_id, int) or user_id <= 0:
raise ValueError(f"Invalid user_id: {user_id}")
try:
return db.query(User).filter(User.id == user_id).first()
except SQLAlchemyError as e:
logger.error(f"Database error fetching user {user_id}: {e}")
raise3. 安全漏洞
AI 在安全方面容易犯的错误:
| 漏洞类型 | 常见场景 | 审查要点 |
|---|---|---|
| SQL 注入 | 字符串拼接构造查询 | 确认使用参数化查询或 ORM |
| XSS | 直接渲染用户输入 | 确认输出有 HTML 转义 |
| 不安全的反序列化 | 直接解析外部 JSON/YAML | 确认有 schema 校验 |
| 硬编码凭证 | 测试代码中的密钥 | 检查是否有字面量密码/密钥 |
| 路径遍历 | 文件操作用用户输入 | 确认路径规范化和限制 |
| 权限缺失 | CRUD 接口未验证所有权 | 检查每个写操作的权限校验 |
4. 过度工程化
AI 有时会生成远超需求复杂度的代码:
typescript
// AI 生成的代码(过度复杂)
// 为了一个简单的"格式化日期"需求,AI 构建了一个完整的策略模式...
interface DateFormatter {
format(date: Date): string;
}
class ISODateFormatter implements DateFormatter { ... }
class LocaleDateFormatter implements DateFormatter { ... }
class DateFormatterFactory { ... }
class DateFormatterRegistry { ... }
// 实际需要的
const formatDate = (date: Date) => date.toLocaleDateString('zh-CN');审查原则:代码复杂度应与问题本身的复杂度匹配。
5. 测试代码中的无效断言
javascript
// AI 生成的测试(问题示例)
test('should create user', async () => {
const result = await createUser({ name: 'Alice', email: 'alice@example.com' });
expect(result).toBeDefined(); // ❌ 这个断言毫无价值,几乎不可能失败
expect(result.id).toBeDefined(); // ❌ 同样过于宽松
});
// 有效的测试
test('should create user with correct fields', async () => {
const result = await createUser({ name: 'Alice', email: 'alice@example.com' });
expect(result.id).toBeGreaterThan(0);
expect(result.name).toBe('Alice');
expect(result.email).toBe('alice@example.com');
expect(result.createdAt).toBeInstanceOf(Date);
});代码审查清单
逻辑正确性
□ 核心业务逻辑是否符合需求规格?
□ 数据流是否按预期方向流动?
□ 条件分支是否覆盖所有情况(含 else/default)?
□ 循环是否有正确的终止条件?
□ 异步操作是否正确处理(await、Promise 链)?
□ 错误处理是否完整(try/catch、错误传播)?安全性
□ 所有外部输入是否经过校验和清洁?
□ 数据库操作是否防止注入攻击?
□ 敏感数据是否避免在日志中输出?
□ API 接口是否有适当的认证和授权?
□ 文件操作是否限制访问范围?
□ 依赖包是否来自可信来源?性能
□ 是否存在 N+1 查询问题?
□ 大数据集是否有分页处理?
□ 是否有不必要的同步阻塞操作?
□ 缓存策略是否合理?
□ 是否存在内存泄漏风险(事件监听、定时器)?可维护性
□ 函数/方法是否遵循单一职责原则?
□ 变量和函数命名是否清晰表达意图?
□ 是否有必要的注释(复杂逻辑、非直观的决策)?
□ 是否有重复代码需要提取?
□ 是否遵循项目已有的代码风格?
□ 是否引入了不必要的新依赖?验收标准定义
好的验收标准应该是具体的、可测量的、可自动化验证的。
验收标准模板
markdown
## 功能验收标准:用户登录
### 功能性验收
- [ ] 正确的邮箱和密码可以成功登录,返回 JWT 令牌
- [ ] 错误的密码返回 401 状态码和明确的错误消息
- [ ] 不存在的邮箱返回 401(不暴露账号是否存在)
- [ ] 登录成功后,令牌在请求头中正确返回
### 安全性验收
- [ ] 连续 5 次失败后账户被临时锁定 15 分钟
- [ ] 密码在数据库中以 bcrypt hash 存储,不可逆
- [ ] JWT 令牌的有效期为 24 小时
- [ ] 令牌中不包含敏感信息(仅含 userId 和 role)
### 性能验收
- [ ] 正常登录响应时间 < 300ms(不含网络延迟)
- [ ] 支持并发 100 个登录请求不出现错误
### 代码质量验收
- [ ] 函数测试覆盖率 > 80%
- [ ] 没有 TypeScript 类型错误
- [ ] 通过 ESLint 静态检查审查工作流
推荐的审查流程
AI 生成代码
↓
1. 快速阅读(2分钟)
- 理解整体结构
- 标记可疑点
↓
2. 运行自动化检查
- 类型检查(tsc)
- Lint 检查(eslint)
- 单元测试(npm test)
↓
3. 针对性深度审查
- 重点检查标记的可疑点
- 安全敏感代码逐行审查
- 验证所有引用的 API 确实存在
↓
4. 功能测试
- 手动测试核心路径
- 测试边界条件
↓
5. 反馈与修正
- 将发现的问题反馈给 AI
- 要求针对性修复(不是重新生成全部代码)与 AI 协作审查
你也可以让另一个 AI 对话来审查代码:
请以高级安全工程师的视角审查以下代码,重点关注:
1. 可能存在的安全漏洞
2. 输入校验是否完整
3. 错误处理是否健壮
[粘贴代码]这种"AI 审查 AI 生成的代码"的模式,能有效捕获一些常见问题,但不能替代人工判断,特别是对业务逻辑正确性的判断。
建立团队审查文化
当团队中多人使用 AI 辅助开发时,建议:
- 将审查清单集成到 PR 模板中,每次提交都必须确认关键检查项
- 设立 AI 代码专项审查员,定期抽查 AI 生成代码的质量
- 记录 AI 常见错误模式,形成团队知识库,指导后续提示词优化
- 不降低测试覆盖率要求,AI 生成代码同样需要完整的测试覆盖