AI代码审查修改指南:15条PR检查清单
AI辅助编码提升效率但增加审查负担,代码表面完整实则可能隐藏需求边界扩大、业务不变量遗漏、未复用现有抽象等风险。需聚焦15个易错点,如权限检查、数据库N+1查询、并发幂等处理,确保变更在正确约束下运行。
AI 辅助编码进入团队三个月后,不少技术负责人(Tech Lead)都会遇到一个反直觉的现象:代码产出速度确实提升了,但代码审查(Review)的负担却没有丝毫减轻。以前,一个 PR 的核心风险主要看“人有没有想清楚”;现在则多了一种新风险——代码看起来完整、命名规范、测试也能通过,但它可能只是在错误的上下文里,拼凑出一套看似合理的实现。

传统的代码审查依赖经验直觉:扫描 diff、检查关键分支、确认命名和边界。但 AI 生成代码的危险点,恰恰在于“表面质量很高”。它会补齐样板代码,会模仿项目风格,还会给出自信满满的注释——可它不一定理解业务不变量、历史兼容约束、线上数据形态和团队约定。因此,审查不能只盯着“代码写得好不好”,更得问一句:“这段代码是否在正确的问题上、以正确的约束运行?”
下面这份清单,不是为了要求审查者做无差别的全面检查,而是把注意力集中到 AI 最容易出错的区域。建议团队直接将其纳入 PR 模板:作者先自查,审查者再按风险挑重点看。
1. 需求边界是否被 AI 擅自扩大
重点检查:这个 PR 是否只解决 issue 中定义的问题?有没有顺手重构、顺手改默认行为、顺手支持“看起来合理”的新场景?
AI 为什么容易犯这个错:LLM 天生倾向于补全“完整方案”,而不是严格执行最小变更。它会主动添加参数、分支和兼容逻辑,使 diff 看起来特别周全,但实际上扩大了发布风险。
❌ 坏例子:修复分页默认值,却顺手改了排序字段。
// 原需求:pageSize 为空时默认 20const pageSize = query.pageSize ?? 20;const orderBy = query.orderBy ?? "created_at"; // AI 顺手新增,改变旧接口默认排序✅ 好例子:只处理明确需求,新增行为单独开 PR。
const pageSize = query.pageSize ?? 20;// 排序规则保持原逻辑,不在本 PR 修改2. 业务不变量是否被显式表达
重点检查:账户余额不能为负、订单状态只能单向流转、权限不能越级——这些规则在代码或测试中明确体现了吗?
AI 为什么容易犯这个错:AI 能推断常见业务流程,但不知道你们系统里的“不允许”。它特别容易写出“技术上可运行、业务上非法”的路径。
❌ 坏例子:只判断状态存在。
if (order.getStatus() != null) {order.setStatus(request.getStatus());}✅ 好例子:编码状态流转约束。
if (!OrderStatus.canTransit(order.getStatus(), request.getStatus())) {throw new BizException("非法订单状态流转");}order.setStatus(request.getStatus());3. 是否复用了项目现有抽象,而不是新造一套
重点检查:日志、鉴权、异常、事务、RPC 客户端、配置读取等横切能力,是否沿用项目已有的封装?
AI 为什么容易犯这个错:模型从通用语料中学习到大量“标准写法”,但不知道当前仓库中已经有现成的工具类。它会重复造轮子,造成长期维护的分叉。
❌ 坏例子:绕过统一异常体系。
if err != nil {return nil, fmt.Errorf("call user service failed: %v", err)}✅ 好例子:使用团队统一错误码和包装方式。
if err != nil {return nil, errors.WrapCode(errcode.UserServiceUna vailable, err)}4. 错误处理是否只是“吞掉异常”
重点检查:catch 之后是否有告警、降级、错误码映射、重试边界?不能只记录日志后就继续往下走。
AI 为什么容易犯这个错:AI 很喜欢生成 try/catch,让代码看起来“健壮”,但它经常不知道异常对业务流程意味着什么,结果就是把失败变成静默的脏数据。
❌ 坏例子:记录日志后返回空结果。
try:profile = client.get_profile(user_id)except Exception as e:logger.warning("get profile failed", exc_info=e)return {}✅ 好例子:区分可降级和不可降级失败。
try:profile = client.get_profile(user_id)except TimeoutError as e:metrics.incr("profile.timeout")return Profile.anonymous(user_id)except AuthError as e:raise PermissionDenied("profile access denied") from e5. 空值和默认值是否改变了语义
重点检查:null、空字符串、空数组、0、false 有没有被混用?默认值和旧逻辑是否一致?
AI 为什么容易犯这个错:模型倾向于使用 ??、||、Optional.orElse 这类简洁写法,但业务中“未传”和“传空”经常代表不同含义。
❌ 坏例子:把 0 当作未传。
const retryTimes = input.retryTimes || 3;✅ 好例子:只对 null/undefined 使用默认值。
const retryTimes = input.retryTimes ?? 3;6. 权限检查是否放在真实执行路径上
重点检查:新增接口、批量操作、导出、异步任务,是否都经过了鉴权?不能只在 Controller 层做一次象征性校验。
AI 为什么容易犯这个错:AI 常按“入口校验”模板写代码,但会忽略内部复用方法可能被其他路径调用。
❌ 坏例子:只有 HTTP 入口检查权限。
@PostMapping("/users/export")public File export() {auth.check("user:export");return userExportService.exportAll();}✅ 好例子:核心服务层也带权限上下文。
public File exportAll(UserContext ctx) {permission.require(ctx, "user:export");return doExport();}7. 数据库查询是否引入隐藏的 N+1
重点检查:循环里是否有查库、调 RPC?ORM 懒加载字段有没有被触发?批量接口真的批量执行了吗?
AI 为什么容易犯这个错:AI 更容易根据“单条数据”的样例补全代码,不会主动推断线上列表页可能包含几百条记录。
❌ 坏例子:循环查用户。
orders.map { order ->val user = userRepo.findById(order.userId)OrderVO(order, user.name)}✅ 好例子:先批量加载再组装。
val users = userRepo.findByIds(orders.map { it.userId }).associateBy { it.id }orders.map { order -> OrderVO(order, users[order.userId]?.name) }8. 并发和幂等是否只覆盖了“单线程快乐路径”
重点检查:重复提交、消息重放、定时任务并发、回调多次到达时,是否安全?
AI 为什么容易犯这个错:模型生成的流程通常是线性的:先查、再改、再保存。它不会天然考虑两个请求同时进来。
❌ 坏例子:先查后插,没有唯一约束兜底。
SELECT id FROM coupon_usage WHERE user_id = ? AND coupon_id = ?;-- not exists then insert✅ 好例子:数据库唯一键 + 冲突处理。
CREATE UNIQUE INDEX uk_coupon_user ON coupon_usage(coupon_id, user_id);INSERT INTO coupon_usage(coupon_id, user_id) VALUES (?, ?)ON CONFLICT DO NOTHING;9. 时间、时区和单位是否明确
重点检查:秒和毫秒、UTC 和本地时区、自然日和 24 小时、过期时间,是否统一了?
AI 为什么容易犯这个错:语料中时间写法太多,AI 很容易混用 Date.now()、Unix 秒和数据库 timestamp。
❌ 坏例子:把毫秒传给要求秒的接口。
cache.expire(key, Date.now() + 3600 * 1000);✅ 好例子:变量名和 API 都标明单位。
const ttlSeconds = 3600;cache.expire(key, ttlSeconds);10. 测试是否只验证了 AI 自己写出的实现
重点检查:测试覆盖了旧 bug、边界条件、失败路径吗?还是只测了 happy path?
AI 为什么容易犯这个错:AI 会根据实现反推测试,导致“实现错,测试也跟着错”。这种测试提高覆盖率,但不会提高信心。
❌ 坏例子:只验证正常输入。
test("create user", () => {expect(createUser({ name: "Tom" }).name).toBe("Tom");});✅ 好例子:测试需求约束和历史 bug。
test("reject duplicated email ignoring case", () => {createUser({ email: "A@EXAMPLE.com" });expect(() => createUser({ email: "a@example.com" })).toThrow("EMAIL_EXISTS");});11. 日志是否会泄露敏感信息
重点检查:token、手机号、邮箱、身份证、Cookie、请求体,有没有被完整打印?
AI 为什么容易犯这个错:AI 常把“方便调试”放在第一位,生成 JSON.stringify(request) 这类全量日志。
❌ 坏例子:打印完整请求。
logger.info("payment request", { body: req.body });✅ 好例子:只打印必要字段并脱敏。
logger.info("payment request", {orderId: req.body.orderId,amount: req.body.amount,phone: maskPhone(req.body.phone)});12. 配置和常量是否硬编码
重点检查:超时时间、重试次数、URL、开关、阈值,是不是写死在代码里了?
AI 为什么容易犯这个错:为了让示例自洽,AI 常直接写一个“合理数字”。但生产系统中这些数字通常需要按环境调整。
❌ 坏例子:代码里写死外部地址。
private static final String API = "https://api.partner.com/v1";✅ 好例子:走配置并给出默认值说明。
@Value("${partner.api.base-url}")private String partnerApiBaseUrl;13. 注释是否在解释错误的事实
重点检查:注释、README、接口文档,是不是和代码一致?尤其要小心 AI 生成的“自信解释”。
AI 为什么容易犯这个错:LLM 很擅长生成听起来合理的注释,但注释可能描述的是它想象中的系统,而不是实际系统。
❌ 坏例子:注释承诺了代码没做到的行为。
# retry 3 times with exponential backoffreturn client.call(payload)✅ 好例子:要么实现,要么删掉虚假注释。
return retry(max_attempts=3, backoff="exponential")(client.call)(payload)14. 依赖升级和新增包是否必要
重点检查:是不是为了几行工具函数就引入新依赖?版本和项目兼容吗?许可证可接受吗?
AI 为什么容易犯这个错:模型会推荐流行包,但不知道你们的依赖治理、镜像源、漏洞扫描和许可证限制。
❌ 坏例子:为了格式化日期新增大型库。
{"dependencies": {"moment": "^2.30.0"}}✅ 好例子:优先使用项目已有工具或标准库。
const date = new Intl.DateTimeFormat("zh-CN", { dateStyle: "short" }).format(value);15. 回滚路径和灰度开关是否清晰
重点检查:变更可关闭、可回滚吗?数据结构变更兼容旧代码吗?发布失败时怎么恢复?
AI 为什么容易犯这个错:AI 关注“如何实现功能”,很少主动考虑发布过程。它会把新逻辑直接接入主路径,让一个小 PR 变成不可逆变更。
❌ 坏例子:新算法直接替换旧算法。
score := newRanker.Score(item)✅ 好例子:用配置开关控制切换,并保留观测指标。
if config.EnableNewRanker {metrics.Incr("ranker.new.used")score = newRanker.Score(item)} else {score = oldRanker.Score(item)}可直接复制的 PR 描述模板
## 变更摘要- 本 PR 解决的问题:- 本 PR 不解决的问题:## 是否含 AI 生成代码- [ ] 否,全部手写- [ ] 是,局部使用 AI 辅助- [ ] 是,核心逻辑由 AI 生成后人工修改使用的 AI 工具 / 模型:## AI 生成代码自查- [ ] 我确认 PR 没有扩大需求边界- [ ] 我确认核心业务不变量已在代码或测试中体现- [ ] 我确认没有绕过项目现有鉴权、异常、日志、配置封装- [ ] 我确认错误处理没有吞异常或静默返回错误默认值- [ ] 我确认空值、默认值、时间单位、时区语义清晰- [ ] 我确认没有新增不必要依赖或硬编码环境配置## 传播风险自评这次变更影响范围:- [ ] 单个内部函数- [ ] 单个接口 / 页面- [ ] 多个服务调用链- [ ] 数据库结构 / 消息格式 / 公共 SDK如果出错,可能影响:- 用户范围:- 数据范围:- 是否可快速回滚:是 / 否- 回滚方式:## Reviewer 重点关注点请重点看:1. 2. 3. ## 测试说明- 单元测试:- 集成测试:- 手工验证:- 未覆盖但已知风险:## 发布与观测- 是否需要灰度开关:是 / 否- 关键监控指标:- 预期日志 / 告警:总结
AI 编程之后,代码审查的核心不再是“帮作者找语法问题”,而是验证代码是否遵守了业务约束、工程约定和发布边界。真正有效的做法,是让作者先用清单把风险暴露出来,再让审查者把精力放在最可能被 AI 忽略的地方。这份清单不要求一次做到完美,但一定要沉淀到 PR 模板和团队共识中。否则,AI 提升的是提交通度,透支的却是审查质量。
游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。
同类文章
跨境物流AI Agent感知决策全链路自动化技术拆解
2026年,跨境物流领域正迎来一场根本性变革——从“被动记录”全面迈向“主动执行”,AI智能体正在重塑整个行业格局。 你可能已经深有体会:每天一到岗,光是后台处理货件创建、物流追踪等重复操作,就要耗费数小时之久。行业调研数据显示,超过60%的运营人员每天至少花费2小时在这些机械性劳动上,而手动操作引
天学会AI应用开发第十二课:从PDF、Word和网页构建RAG
上一篇文章介绍了如何从TXT文件中读取文本内容,但在日常办公场景中,纯文本文件的使用频率并不高。更常见的需求是处理PDF、Word等文档格式,同时还需要从网页中提取知识信息。 因此,本篇文章将重点讲解如何从PDF、Word以及网页中提取内容,并将其应用于RAG(检索增强生成)系统。 一、从PDF文件
Hy3+WorkBuddy组合国产顶级Agent附完整提示词
五个 Case 跑完,总结一下整体体验。工具使用能力:能不能自己开网页、找信息、标出处。规划能力:能不能把多约束需求拆成可执行步骤。长程执行能力:跨多步任务时会不会丢状态。复杂推理能力:能不能先推导、再写代码、再执行验证。WorkBuddy+Hy3 的表现完全符合预期。趁着 WorkBuddy 里的
AI Agent是什么?一文理解大语言模型、记忆、技能、工具、MCP、工作流与上下文
智能体并非单一模型,而是由大语言模型、记忆、工具、工作流等模块协同构成的自主系统。它通过理解目标、检索记忆、调用工具、构建上下文、推理决策,并基于反馈闭环持续迭代,最终自主完成复杂任务。
DeepSeek决定自研芯片打造人工智能全新算力芯脏
被“逼”出来的第三条路。 一直以模型技术见长的DeepSeek,这次在算力供应链上迈出了让所有人侧目的一步。 2026年7月7日,路透社援引三位知情人士消息,DeepSeek正在悄然开发自有AI芯片。值得玩味的是,这颗芯片的定位非常精准——专攻推理,不涉足训练。消息人士称,项目大约启动于一年前,目前
- 热门数据榜
相关攻略
2026-07-11 16:50
2026-07-11 16:50
2026-07-11 16:50
2026-07-11 16:50
2026-07-11 16:28
2026-07-11 16:28
2026-07-11 14:38
2026-07-11 14:38
热门教程
- 游戏攻略
- 安卓教程
- 苹果教程
- 电脑教程

