Skip to content

代码审查与验收标准

为什么 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}")
        raise

3. 安全漏洞

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 辅助开发时,建议:

  1. 将审查清单集成到 PR 模板中,每次提交都必须确认关键检查项
  2. 设立 AI 代码专项审查员,定期抽查 AI 生成代码的质量
  3. 记录 AI 常见错误模式,形成团队知识库,指导后续提示词优化
  4. 不降低测试覆盖率要求,AI 生成代码同样需要完整的测试覆盖