导读:知乎上"AI 写代码太快,Review 不过来,要不要放弃 Review"这个问题有近 20 万浏览。我的看法是 Review 不能省,但第一遍可以交给工具。Claude Code 自带一个
/code-review命令,能在本地审当前改动。本文在 Claude Code 2.1.299 上用一段预埋了 4 个 bug 的 Java 代码实测:不同档位找到了什么、花了多少钱、--fix自动改了哪些、刻意没改哪些。
一、先说清楚:/code-review 是什么
/code-review 是 Claude Code 内置的一个命令(官方文档里归类为 Skill,也就是一段预置的审查流程)。在项目目录里敲它,Claude 会读你当前的改动,找会导致结果出错的 bug,按"文件:行号 + 问题 + 怎么出错"列出来。加 --fix 还能直接把问题改掉。
可以把它理解成一个随叫随到的初审同事:你还没提交、还没开 PR,它先过一遍,把空指针、算错的分页、字符串用 == 比较这类低级但致命的问题挑出来,人再去看设计和业务逻辑。
不用它会怎样?AI 生成代码的速度上来以后,人工 Review 很容易变成扫一眼就过。这类 bug 编译能过、单测不一定覆盖,上线后才暴露。
什么时候值得用:
| 场景 | 建议 |
|---|---|
| 让 Claude 写完一段功能,准备提交 | 每次都跑一遍 low 档,十几秒的事 |
| 改动涉及钱、权限、SQL、并发 | 用 high 档,多花一点换更全的问题清单 |
| 团队 PR 要留痕 | 用 --comment 把结果贴到 PR 上(本文没实测) |
| 一个大分支要合主干 | 考虑云端的 /code-review ultra(本文没实测,见第六节) |
二、实测环境和测试代码
| 项 | 版本 |
|---|---|
| Claude Code | 2.1.299 |
| 模型 | claude-opus-5-5(JSON 输出里的 modelUsage 字段) |
| 系统 | macOS 15 |
| 运行方式 | 非交互模式 claude -p,方便记录输出和费用 |
我在 scratchpad 临时目录建了一个 git 仓库,main 上是一个正常的 OrderService,然后切一个 feature/batch-cancel 分支,提交了两个新方法,里面故意埋了 4 个 bug:
/** 按状态分页查询用户订单,page 从 1 开始 */
public List<Order> pageByStatus(String userId, String status, int page, int size) {
// bug 1:参数直接拼进 SQL,SQL 注入
// bug 2:注释说 page 从 1 开始,offset 却是 page * size,第一页永远查不到
String sql = "select * from t_order where user_id = '" + userId + "' and status = '" + status
+ "' limit " + (page * size) + "," + size;
return orderMapper.selectBySql(sql);
}
/** 统计用户已支付订单的优惠总额 */
public BigDecimal totalDiscount(String userId) {
BigDecimal sum = BigDecimal.ZERO;
for (Order order : orderMapper.selectByUser(userId)) {
// bug 3:字符串用 == 比较
if (order.status() == "PAID") {
// bug 4:BigDecimal 不可变,add 的返回值被丢掉,永远返回 0
sum.add(order.discount());
}
}
return sum;
}
正文里的代码带了注释方便读者看,实测时提交的版本没有这些 bug 注释,免得给 Claude 提示。这 4 个都是 Java 后端 Code Review 里很常见的问题,编译都能过。
三、基本用法:low 档跑一遍
在仓库目录里执行(交互模式下直接输入 /code-review low 是一样的):
# 在要审查的 git 仓库根目录执行
# --allowedTools 只放行读文件和 git 命令,审查阶段不需要改文件
claude -p "/code-review low" --allowedTools "Bash(git:*)" "Read" "Grep" "Glob"
当前分支的改动都已经提交、工作区是干净的,它审的是这个分支相对 main 的提交。约 14 秒后输出:
src/main/java/demo/OrderService.java:31 — 注释写明 page 从 1 开始,但 offset 用的是 `page * size`。结果 page=1 会跳过第一页,从第 `size` 条开始返回,第一页数据永远查不到。应改为 `(page - 1) * size`。
src/main/java/demo/OrderService.java:30 — `userId` 和 `status` 直接拼进 SQL 字符串,存在 SQL 注入风险。比如传入 `x' or '1'='1` 就能查到所有用户的订单。
src/main/java/demo/OrderService.java:40 — `BigDecimal` 是不可变对象,`sum.add(order.discount())` 的返回值被丢掉了,所以 `totalDiscount` 永远返回 0。应改为 `sum = sum.add(...)`。
src/main/java/demo/OrderService.java:39 — 字符串用 `==` 比较的是引用,不是内容。从 DB 映射来的 status 跟字面量 `"PAID"` 不是同一个对象,条件会一直为 false,已支付订单不会被统计。应改为 `"PAID".equals(order.status())`。
4 个 bug 全中,行号都对。每条除了说"哪里错",还说了"会出什么后果",这一点比很多人工 Review 的"这里有问题"要好用。
💡 输出是中文,应该和我本机全局 CLAUDE.md 设了默认中文回复有关。但下面 high 档那次输出的是英文 JSON 列表,同样的环境,格式和语言并不完全固定,写脚本解析它的输出要留意。
四、effort 档位:low 和 high 差在哪
/code-review 后面可以跟一个档位:low | medium | high | xhigh | max | ultra。前五个是本地审查的力度,越往后想得越深;ultra 是另一回事,会把审查发到云端跑多个 Agent(第六节)。
同一份代码,我用 --output-format json 分别跑了 low 和 high,拿到耗时和费用:
# 加 --output-format json,结果里有 duration_ms 和 total_cost_usd
claude -p "/code-review high" --allowedTools "Bash(git:*)" "Read" "Grep" "Glob" --output-format json
结果对比:
| 档位 | 耗时 | 费用(total_cost_usd) | 找到的问题 |
|---|---|---|---|
| low | 约 9 秒 | 0.26 美元 | 4 个,正好是预埋的 4 个 |
| high | 约 36 秒 | 0.38 美元 | 8 个:预埋的 4 个 + 另外 4 个 |
high 多出来的 4 个,摘要翻译过来是:
| 行 | 问题 |
|---|---|
| 31 | page、size 没校验,page=0 或负数会拼出 limit -10,10,page*size 是 int 运算还可能溢出 |
| 40 | 前面的 bug 修好之后,如果某条 PAID 订单的 discount 是 NULL,sum.add(null) 会抛空指针 |
| 32 | 绕过 Mapper 走通用的 selectBySql 拼 SQL,应该加一个用 #{} 绑定参数的 Mapper 方法 |
| 38 | 为了求和把用户全部订单查到内存里,应该一条 select sum(discount) ... where status='PAID' 搞定 |
第二条我觉得最有价值:它推演了"修好之后会发生什么"。这种连环问题人工 Review 时很容易漏,因为看代码的人一般只盯眼前这一个错。后两条已经算设计建议,不是 bug,有人会觉得是噪音。
⚠️ 费用和耗时只各跑了一次,和代码量、缓存命中、账号计费方式都有关,只能看相对大小,别当成固定价格。low 档那次耗时 9 秒,另一次不带 json 的 low 档用了约 14 秒。
我的建议:日常提交前用 low,涉及钱、SQL、权限、并发的改动用 high。xhigh 和 max 我这次没测,不清楚和 high 的差距。
五、--fix:让它直接改,以及它没改什么
# 加 --fix 后需要放行 Edit,否则它只能报告不能改
claude -p "/code-review low --fix" --allowedTools "Bash(git:*)" "Read" "Grep" "Glob" "Edit"
约 20 秒后的输出:
Review 发现 4 个问题,已修好 3 个,SQL 注入那条没改。都在 `src/main/java/demo/OrderService.java`,没编译也没跑测试。
1. `:30` — `pageByStatus` 用字符串拼接 `userId` 和 `status` 生成 SQL,存在 SQL 注入:传入 `' or '1'='1` 就能查出任意用户的订单。**未修复**:要在 `OrderMapper` 新增一个参数化查询方法,改动超出本次 diff 的范围。
2. `:31` — 注释写 page 从 1 开始,offset 却用的 `page * size`。结果 page=1 时直接跳过第一页,最后一页也查不到。**已修复**:改成 `(page - 1) * size`。
3. `:39` — 用 `==` 比较字符串 `"PAID"`。从数据库映射出来的 status 和这个字面量通常不是同一个对象,判断基本总是 false。**已修复**:改成 `"PAID".equals(order.status())`。
4. `:40` — `BigDecimal` 是不可变的,`sum.add(...)` 的返回值被丢掉了,所以 `totalDiscount` 永远返回 0。**已修复**:改成 `sum = sum.add(order.discount())`。
git diff 看实际改动,只动了 3 行:
- + "' limit " + (page * size) + "," + size;
+ + "' limit " + ((page - 1) * size) + "," + size;
...
- if (order.status() == "PAID") {
- sum.add(order.discount());
+ if ("PAID".equals(order.status())) {
+ sum = sum.add(order.discount());
几个值得注意的地方:
- SQL 注入它没动,理由是要改 Mapper 接口,超出了本次改动的范围。这个判断我认同:自动修复去改接口签名,影响面就不只是这个文件了
- 改动在工作区,没有提交。不满意直接
git checkout -- .撤掉 - 它自己说了"没编译也没跑测试"。
--fix之后跑一遍单测还是你的事
⚠️
--fix是直接改文件。工作区里有没提交的其他改动时,先 commit 或 stash,不然它的改动和你的混在一起不好分。
六、其他参数和相关命令
6.1 --max-findings:控制条数
# 最多报 2 条
claude -p "/code-review low --max-findings 2" --allowedTools "Bash(git:*)" "Read" "Grep" "Glob"
实测它没有丢掉问题,而是把 4 个问题合并成了 2 条:第一条讲 BigDecimal 和 ==,第二条讲分页和 SQL 注入。官方文档里这个参数的取值是 n|all|default。条数限制适合大改动只想先看最严重的几条;想看全部就传 all。
6.2 指定审查对象
不带目标时审当前改动。官方文档写明后面还可以跟 PR 号、分支或路径:
# 以下写法来自官方命令文档,本文只实测了不带目标的情况
/code-review high 1234
/code-review medium feature/batch-cancel
/code-review low src/main/java/demo
6.3 几个容易混的命令
| 命令 | 在哪跑 | 干什么 | 本文是否实测 |
|---|---|---|---|
/code-review | 本地 | 找正确性 bug,可 --fix、--comment | 是 |
/review | 本地 | /code-review 的别名(官方文档) | 否 |
/code-review ultra / /ultrareview | 云端沙箱 | 多 Agent 深度审查;文档写 Pro 和 Max 含 3 次免费,之后要用量额度 | 否 |
claude ultrareview | 命令行触发云端 | 同上,--json 输出原始结果,--post 贴到 PR | 只看了 --help |
/security-review | 本地 | 专查安全漏洞,对比当前分支和 origin 默认分支,需要有 origin 远端 | 否 |
| GitHub 上的 Code Review | GitHub PR | 每个 PR 自动审并行内评论,文档写面向 Team 和 Enterprise | 否 |
💡 老版本资料里常提到的
/review现在是/code-review的别名,用哪个都行。
七、接进日常流程
我的建议是两层:
- 提交前本地跑 low。在 CLAUDE.md 里加一句约定,让 Claude 写完代码后自己先跑一遍:
## 提交前检查
- 完成一个功能、准备提交前,先运行 /code-review low,把报告给我看,不要自动 --fix
- 改动涉及金额、SQL、权限、并发时,用 /code-review high
- 合主干前用 high 或 ultra,结果用
--comment贴到 PR 上留痕。
两条使用习惯:
- 把它的报告当"待确认清单",不是结论。第四节那种设计建议要人判断
- 别让它审自己刚写的代码就算完事。人工 Review 的重点放在需求理解、接口设计这些工具不擅长的地方
八、速查表
| 想做什么 | 命令 |
|---|---|
| 快速审当前改动 | /code-review low |
| 审得更细 | /code-review high |
| 审完直接改 | /code-review low --fix |
| 只看最多 N 条 | /code-review low --max-findings 2 |
| 看全部问题 | /code-review high --max-findings all |
| 审某个 PR | /code-review high 1234 |
| 把结果贴到 PR | /code-review --comment |
| 云端多 Agent 深审 | /code-review ultra 或 claude ultrareview |
| 专查安全问题 | /security-review |
| 非交互模式拿费用 | claude -p "/code-review low" --output-format json |
九、常见问题
Q:非交互模式下它会自己去改文件吗?
不加 --fix 不会。我在 --allowedTools 里只放了读文件和 git,跑完 git status 看源码没有任何改动。
Q:工作区有没提交的改动时审什么?
官方说法是审"当前 diff"。本文只测了改动全部提交、在分支上的情况,没提交的改动混在一起时具体怎么取范围,我没单独验证。
Q:它能代替人工 Review 吗?
我不建议。它擅长挑实现层面的 bug,比如本文这 4 个;需求理解错了、接口设计不合理,它看不出来,因为它不知道你们的业务约定。
Q:一次要花多少钱?
本文这个 18 行的改动,JSON 输出里的 total_cost_usd 是 low 约 0.26 美元、high 约 0.38 美元,各跑一次。改动越大越贵;订阅账号和 API 账号的计费方式不同,以自己账号的用量页为准。
参考链接
- Claude Code 命令列表(/code-review、/ultrareview、/security-review)
- Claude Code 文档:ultrareview 云端审查
- Claude Code 文档:GitHub Code Review
- 知乎:AI 写代码太快,Review 不过来,要不要违背祖训放弃 Review?
你现在提交前会让 AI 先审一遍吗?用的是
/code-review、别的工具,还是全靠人?如果用过--fix,它有没有改出过新问题?评论区说说,典型情况我补进 FAQ。觉得有用的话点个赞、收个藏,下次提交前翻得到。