去年年底,一位前同事在微信上噼里啪啦发来一串消息:“哥,你用过 claude code 做 code review 吗?我们组搞 CI 流水线接进了 claude code,自动审查性能问题,一开始大家都觉得牛逼,结果连着一周,它逮着几个循环里拼字符串的用法狂报 critical,却对一个明晃晃的 N+1 查询视而不见。”
他补了一句:“你们搞性能工程的不会也在用这玩意儿吧?它到底行不行?”
我当时没有直接回答。作为干了十年性能工程的人,我对“AI 取代代码审查”这类说法始终抱着一种职业性的怀疑。但我也很清楚,今年年初开始,claude code 这种工具在开发者圈子里已经不是一个“尝鲜玩具”了,很多团队的 CI 流水线里确实接入了它,甚至有人把自己的 PR 先扔给它过一遍,再让同事看。用户的反馈两极分化,有人说它“比初级工程师强多了”,也有人直接甩出关键词:“claude code 老是骗人”。
这中间到底发生了什么?claude code 对那些真正影响系统性能的瓶颈,是真懂还是装懂? 那些被它频频指出的“性能问题”,到底是救命良药,还是狼来了式的噪音?
我决定不再靠二手信息判断。接下来两个月,我设计了一套专门针对性能瓶颈识别的测试方案,用 6 组精心构造的“陷阱代码”,加上一段来自真实线上项目的复杂业务逻辑,让 claude code 和一位有着 12 年经验的资深性能工程师同台竞技。这篇文章就是那次实测的完整记录。
一、核心结论:claude code 不是“骗人”,而是在演你
先说结论。经过 6 组定向测试和 30+ 个真实代码片段的交叉验证,claude code 在代码审查中对性能瓶颈的识别能力呈现出三个非常明确的特点:
- 它对模式化的性能反模式极度敏感,识别速度快、覆盖率高,甚至超越了人类审查员的耐心极限。 比如 N+1 查询、循环内创建对象、不必要的深度拷贝、同步阻塞调用这类“教科书式”的错误,它几乎一抓一个准,而且极少漏报。
- 对于需要上下文推理、跨模块关联分析或领域知识注入的性能瓶颈,它的表现直线下跌,不仅容易漏报,甚至会产生极具迷惑性的“建议”,让不熟悉业务上下文而盲从的开发者引入更严重的性能倒退。 这就是不少人抱怨它“骗人”的根源。
- 它的表现严重依赖于 Prompt 的质量和上下文窗口大小。 不加引导时,它像一个刚毕业的计算机系学生,背熟了所有面试宝典,但缺乏工程判断力;而如果给予清晰的审查指令和上下文约束,它又能摇身一变,成为一位给力的第一轮扫描器,大大缩短人类审查员在机械性筛查上耗费的时间。
换句话说,claude code 不是一个可以替代资深工程师的“银弹”,但如果你懂得怎么用它,它确实可以帮你省下大量在“低级错误”上耗费的时间,让你把精力集中在真正需要人脑推理的性能架构决策上。 而如果你盲目地对它的每个建议都照单全收,那最后你可能不是在优化性能,而是在给系统安装定时炸弹。
接下来,我会把测试的全过程、每一个案例的表现、以及我基于这些结果提出的一整套“人机协作代码审查工作流”完整呈现出来。
二、我是如何设计这场“性能考试”的?
测试 AI 工具最怕“随机”。今天拿一段代码扔进去,看它说什么,明天换一段,再胡乱记录一下结论,这种测法毫无价值。要真正衡量 claude code 在性能瓶颈识别上的能力,我必须设计一套可以进行量化比较的测试基准。
1. 测试环境
- 工具版本: claude code(当时最新稳定版,通过 Anthropic 的 API 接入,使用 claude-3-opus 作为后端模型,temperature 设置为 0.1 以最大程度保证输出一致性)。
- 代码语言: Java 17(覆盖最常见的企业级后端场景)和 Python 3.11(关注数据处理与科学计算场景)。选择两种语言是为了观察工具的跨语言一致性。
- 代码规模: 针对性测试用例每个包含 80-200 行代码,嵌入了 1-2 个性能问题。最后还引入了一个来自真实项目的“遗产模块”,包含 2100 行代码,内有多个已知的已知性能瓶颈(已由团队修复并记录),用于观察其在真实复杂度下的表现。
- 对比基线: 一位 12 年经验的资深性能工程师(后文简称“老张”),独立审查同样的代码片段,不设时间限制。我们会记录他发现每个问题所用的时间、报告的问题数量及准确性。
2. 测试用例设计:六道“送命”题和一道综合大题
我参考了过去十年我团队处理过的线上性能事故,以及《Effective Java》《高性能 MySQL》《Python 高性能编程》等书籍中反复出现的高频性能反模式,精心设计了 6 组针对性测试用例,每组代码刻意嵌入一种典型的性能瓶颈。这 6 组分别是:
测试用例 A:N+1 查询(Java,Spring Data JPA)
一组简单的订单-订单明细查询,使用了一对多关联且在没有开启批量抓取的情况下循环调用明细对象,造成典型的 N+1 数据库查询。问题是嵌入在业务逻辑流中的,不是赤裸裸的 for 循环里打印 SQL。
测试用例 B:循环内创建对象(Java,Stream API 滥用)
一段处理 CSV 数据转换的代码,在 flatMap 中每行创建一个 SimpleDateFormat 实例。这是 Java 中最常见的性能反模式之一,但表面形式是通过 Stream 表达,而非显式 for 循环。
测试用例 C:不必要的深度拷贝(Python,NumPy 数组操作)
数据处理脚本中,通过直接赋值切片本可以共享内存,却调用了 .copy() 方法,产生了不必要的全量拷贝,且拷贝发生在循环体内,导致内存分配和拷贝开销随数据量线性增大。
测试用例 D:缺少缓存策略的计算函数(Python,递归 + Memoization 缺失)
经典的斐波那契数列计算,但没有使用 @lru_cache 或字典缓存。这个问题非常简单,但考验的是 AI 能否识别出“重复计算”这一性能杀手,并给出加缓存的建议。
测试用例 E:同步阻塞 I/O 在事件循环中(Python,asyncio 错误用法)
在一个 asyncio 协程中直接调用了 time.sleep() 而不是 await asyncio.sleep(),导致事件循环阻塞。这个案例考验 AI 对并发模型的理解。
测试用例 F:JSON 序列化反序列化的低效用法(Java,Jackson ObjectMapper 重复创建)
代码中每次请求时都 new ObjectMapper(),而不是复用单例。这是 Java Web 应用中非常典型的性能陷阱,每次实例化 ObjectMapper 的成本很高,而且会影响 JIT 优化。
除了这 6 组定向测试,我还准备了一组真实项目中的“遗产模块”,其中已知包含 4 个性能问题:一个在循环内拼接 SQL 进行批量插入(应使用 batch insert)、一个在流处理中频繁创建正则表达式 Pattern 对象、一个未使用分页导致大结果集直接加载到内存、一个在锁内执行日志记录导致锁持有时间过长。这些问题都已由老张在之前的回归审查中发现并修复,所以作为我们的“标准答案”。
3. 审查流程设计
对于 claude code,每一组用例我都采用两种审查模式进行测试:
- 无引导模式: 直接将代码贴在对话中,要求:“请审查这段代码中可能存在的性能问题。”不提供任何额外上下文或提示。
- 定向引导模式: 我会写一段详细的审查指令,例如:“请重点检查这段代码中是否包含 N+1 查询、不必要的对象创建、IO 阻塞、缓存缺失等常见的性能瓶颈。对每个发现请说明具体位置、严重级别,并给出优化建议。”
对于老张,我仅告知他“这是用于评估 AI 工具的一个测试”,让他独立审查,不催促。
4. 评估指标
我选取了四个关键指标来衡量 claude code 的表现:
- 召回率(Recall):所有存在的真实性能问题中,它发现了多少个?高召回率意味着它很少漏报关键问题。
- 准确率(Precision):它报告的所有问题中,真实存在且确实是性能问题的比例。低准确率意味着它容易产生“狼来了”式的误报。
- 平均识别时间:从输入代码到给出第一个正确问题定位的耗时(对于老张则记录他从阅读代码到发现第一个问题的时间)。
- 建议的有效性与可解释性:它是一种主观评价,评估它给出的修改建议是否真正能解决问题,以及解释是否合理、是否有误导性。
这四项指标构成了我评估 claude code 是否“有用”的核心标准,一个只会刷存在感的工具,召回率再高也没用;一个总是指鹿为马的工具,准确率再高也只是巧合。
三、实测现场:六道题,claude code 答得如何?
以下我将逐题展示测试结果。每一个案例,我都会给出真实的代码片段(简化版)、claude code 的原始回复摘要、老张的发现,以及我的分析。

1. 测试 A:N+1 查询,教科书式的胜利
代码片段(简化):
public class OrderService {
@Autowired private OrderRepository orderRepo;
public List<OrderDTO> getOrderDetails(Long customerId) {
List<Order> orders = orderRepo.findByCustomerId(customerId);
return orders.stream().map(order -> {
OrderDTO dto = new OrderDTO();
dto.setOrderId(order.getId());
dto.setItems(order.getItems().stream().map(item -> {
return new ItemDTO(item.getName(), item.getPrice());
}).collect(Collectors.toList()));
return dto;
}).collect(Collectors.toList());
}
}
人类专家老张的审查: 用时 2 分钟,一眼看出:“order.getItems() 是在懒加载下触发额外查询。N+1,妥妥的。解决方案:要么用 JOIN FETCH 在查询时带上 items,要么开启批量抓取。” 同时他补充:“这里 stream 里对每个 item 都 new ItemDTO,对象创建次数取决于 items 数量,如果数量不大其实不是主要问题。” 他的判断是:N+1 是核心瓶颈,对象创建是次要的。
Claude Code 无引导模式: 用时约 18 秒。它的回复:“发现了潜在的 N+1 查询问题……建议使用 JOIN FETCH 或 @EntityGraph 来一次性加载 items……此外,stream 中为每个 item 创建新的 ItemDTO 对象可能产生不必要的开销,如果 item 数量巨大,可以考虑使用 builder 或直接复用对象……” 接着它还附加了一个关于“使用并行流可能提升吞吐量”的建议。
Claude Code 定向引导模式: 用时 12 秒。回复更聚焦:“发现一处严重性能瓶颈:N+1 查询……推荐修改查询方法……” 它没有再提并行流的事情。
我的分析: 对于 N+1 这种模式高度明确的性能反模式,claude code 在两个模式下都表现完美,召回率 100%,准确率 100%。它甚至分析出了懒加载的机制和对应的 ORM 修复方案。有意思的是,在无引导模式下,它还顺便提出了“对象创建”和“并行流”的辅助建议。前者有一定道理(但未区分主次),后者在典型 Web 环境下其实是危险的建议(并行流使用 ForkJoinPool,可能与容器线程池冲突),这就是后面要讨论的“附加建议的毒性”。
建议的有效性评价: 核心建议(JOIN FETCH)完全有效且正确。附加建议有误导风险,尤其是并行流。因此对于这个测试用例,如果用户能分辨建议的主次,那 claude code 就是一个优秀的一审;如果用户不分轻重全部采纳,反而可能引入新坑。
2. 测试 B:循环内创建 SimpleDateFormat,模式匹配的巅峰,与分析深度的瓶颈
代码片段(简化):
public List parseDates(List dateStrs) {
return dateStrs.stream()
.map(s -> {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try {
return sdf.parse(s).toString();
} catch (ParseException e) {
return null;
}
})
.collect(Collectors.toList());
}
老张的审查: 30 秒发现:“SimpleDateFormat 是线程不安全的,但这里是在单线程 stream 里用,没有线程安全问题。不过每次 map 都 new 一个,这是典型的重复创建昂贵对象。推荐提取到外部或者使用 ThreadLocal,JDK 8 还可以用 DateTimeFormatter,那个是不可变且线程安全的。” 老张还额外指出:“这段代码的逻辑其实有个隐藏的性能隐患:如果输入 list 很大,stream 处理会创建大量的中间 String 对象,不过这是次要的。”
Claude Code 无引导模式: 18 秒,它的回复:“在流处理中,每次 map 操作都创建了一个 SimpleDateFormat 实例,这是一个昂贵且不必要的开销。SimpleDateFormat 的创建成本较高,应将其提取到外部……此外,注意到 SimpleDateFormat 不是线程安全的,如果在并行流中使用会出现问题。” 然后它又补了一条:“建议考虑使用 DateTimeFormatter,它是不可变且线程安全的,性能更好。” 最后它还说:“另外,这段代码在流中捕获 ParseException 并返回 null,可能掩盖了错误,建议使用 Optional……”
定向引导模式: 15 秒,回复更精简,聚焦在 SimpleDateFormat 的重复创建和替代方案。没有再提异常处理和 Optional。
我的分析: 召回率 100%,准确率 80%。因为除了 SimpleDateFormat,无引导模式还提到了异常处理和 Optional,这些属于代码质量和健壮性建议,而不是性能瓶颈,如果以纯粹的性能审查为目标,这些属于“误报”(不是错误,而是超出范围)。然而,claude code 在这里展现了一个极高的价值:它自动给出了 DateTimeFormatter 的替代方案,甚至解释了不可变性和线程安全性。这种从一个点延伸到最佳实践的推理,是典型的“背下了所有最佳实践,并在合适的场合应用了它”。对经验不足的开发者,这个建议非常有启发性,但也埋下了一个隐患:它可能让开发者过度信任 AI 的所有建议。
独特视角: 老张在分析时主动区分了“主要问题(重复创建)”和“次要问题(中间 String 对象)”,这是基于工程经验的判断,在绝大多数场景下,重复创建 SimpleDateFormat 是真正的瓶颈,而中间对象是 JVM 层面容易优化的问题。claude code 没有这种主次分级能力,它的建议权重是扁平的。这是 AI 与人类专家最大的差距之一:性能瓶颈的严重性排序。
3. 测试 C:不必要的深度拷贝,AI 看懂了语法,没看懂意图
Python 代码片段(简化):
def process_data(data_matrix):
假设 data_matrix 是一个大型 numpy 数组
for i in range(data_matrix.shape[0]):
row = data_matrix[i, :].copy()
假设对这个 row 进行一些复杂的计算
processed = compute_heavy(row)
data_matrix[i, :] = processed
return data_matrix
老张的审查: 约 1 分钟。他一眼看出:“这里 row = data_matrix[i, :].copy() 是完全不必要的。data_matrix[i, :] 返回的是一个视图,如果你只是读取计算,不会有任何拷贝。调 .copy() 反而强制创建了一个新数组,如果矩阵很大,每次循环都分配内存并拷贝,性能会很糟。应该直接去掉 .copy()。” 他还补了一句:“不过如果 compute_heavy 里面会修改原数组,那可能需要拷贝,但从代码上下文看,似乎只是计算一个新值赋回去,不涉及原地修改。”
Claude Code 无引导模式: 20 秒,它的回复:“在循环中调用 .copy() 可能会导致大量内存分配和数据复制,特别是当 data_matrix 很大时。如果计算不改变原始数据,建议直接使用切片视图。如果必须保护原数据,应考虑在循环外一次性复制。” 但它紧接着又说:“另外,注意到循环直接在 numpy 数组上按行迭代,如果是纯 Python 循环,可能效率不高,建议考虑向量化操作……” 然后它给了一个向量化的建议示例。
定向引导模式: 18 秒,回复更直接:“发现潜在的性能瓶颈:循环内不必要的 numpy 数组拷贝。建议移除 .copy(),除非需要独立副本。”
我的分析: claude code 发现了不必要的拷贝问题,召回率 100%。但它同时又给了向量化建议,而向量化建议在真实代码中并不总是容易实现的(取决于 compute_heavy 的复杂性)。这就是一种典型的“AI 多此一举”。从性能审查的准确率来看,这条向量化建议如果在用户没有要求优化的情况下被提出,其实形成了误报,因为它暗示当前的写法不够好,而实际上在 compute_heavy 不是能轻易向量化的场景下,这是一个合理的选择。
更值得注意的是,claude code 没有像老张那样深入分析“compute_heavy 是否修改原数组”这一关键上下文。它只是基于模式匹配出“.copy() 在循环里不好”,它并没有真正“推理”这段代码的业务意图。 也就是说,它是靠记忆模式来做判断,而不是理解数据流。这种局限性在很多看似成功的识别背后都存在。

4. 测试 D:缺失缓存的计算函数,基础能力扎实,但未必理解为什么那么做
Python 代码(斐波那契):
def fib(n):
if n <= 1:
return n
return fib(n-1) + fib(n-2)
老张的审查: 10 秒:“时间复杂度指数级,当 n>30 就明显卡。加个 lru_cache 装饰器或者整一个字典缓存结果。这是经典递归问题,没啥好说的。”
Claude Code 无引导模式: 8 秒,它立刻指出:“此递归实现时间复杂度为 O(2^n),对于较大的 n 性能极差。建议使用 functools.lru_cache 进行记忆化,或转换为迭代版本。” 然后给出了代码示例。
这题毫无悬念,召回率和准确率双 100%。它甚至给出了迭代版本的代码,展示了多角度思考能力。
但这个用例也揭示了一个观察:claude code 之所以能秒杀这个问题,是因为这个问题在训练数据里出现次数太多了。 斐波那契递归是几乎所有编程教程和面试题都会讲的内容,AI 已经记住了标准答案。这不能说明它有深刻的算法分析能力,但它确实能帮助那些遗忘基础的程序员快速发现问题。
5. 测试 E:asyncio 中的同步阻塞,它懂并发模型,但未必懂你的业务模型
Python 代码:
async def fetch_data(urls):
for url in urls:
模拟请求间的延迟
time.sleep(0.1)
responses = await asyncio.gather(*[async_request(u) for u in urls])
return responses
老张的审查: 较快指出:“time.sleep(0.1) 在协程中是阻塞的,会卡住整个事件循环,应该用 await asyncio.sleep(0.1)。不过这个循环逻辑本身也很奇怪,在循环里每次都对整个 urls 做 gather,外层循环会重复执行多次,可能是逻辑错误。性能上除了 sleep 的阻塞,还有重复请求的问题。” 他深入分析了代码意图,指出这可能是两层错误。
Claude Code 无引导模式: 约 9 秒,它精准抓住了:“在 async 函数中使用 time.sleep() 会阻塞事件循环,应该使用 await asyncio.sleep()。” 然后也顺便指出了循环逻辑可能是一个 bug,但语气弱一些:“请注意,循环中每次迭代都对相同的 urls 调用 asyncio.gather,如果您只想执行一次,请考虑调整逻辑。”
我的分析: claude code 对 asyncio 阻塞问题识别得非常准确,这得益于它对 Python 并发模型的模式匹配能力。而且它还能附带逻辑检查,这反而提升了它的价值。这体现了 AI 的一个优势:可以在审查性能的同时,不经意间帮你揪出逻辑 bug。 这其实是很多团队喜欢把它接入 CI 的原因,它像一个不知疲倦的多面手。
6. 测试 F:ObjectMapper 重复创建,模式识别 + 精确的最佳实践
Java 代码片段(Spring controller):
@RestController
public class UserController {
@PostMapping("/user")
public ResponseEntity<String> createUser(@RequestBody String userJson) {
ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(userJson, User.class);
// ... 保存逻辑
return ResponseEntity.ok("success");
}
}
老张: 瞬间指出:“每次请求都 new ObjectMapper,这是典型的浪费。ObjectMapper 是线程安全的,应该做成单例,注入到 controller。”
Claude Code: 同样迅速发现,并建议使用 Spring 的 @Bean 或静态初始化。召回率 100%,准确率 100%。
至此,六道基础题,claude code 在定向引导模式下对所有真实性能瓶颈实现了 100% 召回,无模式模式则额外引入了一些无关建议导致准确率下降。它像一个背熟所有《高性能开发手册》的初级工程师,凡是书上写的反模式,它基本都能认出来。
四、真正的试金石:遗产模块中的“隐藏题”
六道针对性题目是开卷考试,因为我知道什么是标准答案。但真实的代码审查不是这样的。接下来我将我在一家电商公司的老项目里抽取的一个“商品推荐引擎模块”丢进了 claude code。这段代码约 2100 行,包含多个类,涉及数据库查询、缓存、算法计算,其中已知有 4 个性能瓶颈(已修复)。
1. 人类专家的审查结果
老张花了大约 35 分钟仔细审查,找出了全部 4 个已知问题,还额外发现了一个潜在风险(某个哈希碰撞退化场景)。他的报告摘要:
- 问题1(循环内拼接 SQL 批量插入),严重。
- 问题2(流中反复创建 Pattern 对象),中等。
- 问题3(未分页查询导致大结果集加载到内存),严重。
- 问题4(锁内日志记录延长锁持有时间),中等。
2. Claude Code 的审查结果
我分两次运行:一次无引导,一次定向引导。
无引导模式下,claude code 只发现了 4 个已知问题中的 2 个(问题2和问题3),同时对问题3的严重性判断不足,它建议“考虑分页”,但没有强调全量加载内存的风险。它还提出了 7 个其他建议,其中 3 个是误报(例如它认为某个 HashMap 应该改成 LinkedHashMap 以提升“遍历性能”,但实际上那个 HashMap 遍历顺序无关紧要,且 LinkedHashMap 还会增加内存占用)。
定向引导模式下,我给的 Prompt 是:“请针对这段代码进行专项的性能瓶颈审查,重点关注以下方面:数据库查询效率、对象创建成本、内存占用、锁竞争、算法复杂度、不必要的 I/O 操作。请对发现的问题按照严重程度分级,给出具体行号和修改建议,并解释为什么这是个瓶颈。” 结果是:发现了全部 4 个真实瓶颈,并且严重程度判断与老张一致。同时它减少了误报,只提了 2 个额外建议,其中一个是关于日志级别检查的最佳实践(不算性能瓶颈,但算有意义的代码质量建议)。

这个测试揭示了一个极其关键的认知:claude code 对性能瓶颈的识别能力,严重受限于你给它的“审查指令”和“上下文大小”。 无引导模式下,它的注意力被分散在整个代码库的各种细节中,无法聚焦性能这一特定维度;而如果你的 Prompt 足够明确,它就能像一个高效的扫描器,精准定位问题。
但这还不是全部。在分析它的建议时,我还发现两个深层次的问题,直接决定了它“会不会骗人”。
五、什么时候它可靠,什么时候它“骗人”?
经过全部测试,我总结出 claude code 在性能审查中的能力边界,可以用一个二维矩阵来描述:
| 场景类型 | 性能瓶颈示例 | Claude Code 表现 | 可靠性评价 |
|---|---|---|---|
| 强模式匹配 | N+1查询、循环创建对象、不必要拷贝、缺失缓存、阻塞IO、资源未复用 | 召回率极高,准确率高 | ⭐⭐⭐⭐⭐ 可高度信任 |
| 弱模式匹配但局部可见 | 算法选择不佳(如 O(n²) 替代 O(n log n))、错误的数据结构选择(ArrayList频繁插入删除) | 依赖简单复杂度分析,能识别明显低效,但对复杂算法无力 | ⭐⭐⭐ 部分可信,需人工验证 |
| 跨模块上下文依赖 | 跨多个类的锁顺序导致死锁风险、消息队列消费者能力不匹配、缓存失效策略导致缓存穿透 | 基本无法识别,除非将所有相关代码一次性提供 | ⭐ 不可信,极易漏报 |
| 需要业务理解 | 不合理的超时设置、数据量估算错误导致的设计缺陷、批处理大小与系统资源不匹配 | 完全无力 | ⭐ 不可信 |

为什么会有“骗人”的感觉?
我那位同事抱怨“老是骗人”,其实背后对应着 Claude Code 三种典型的“犯错模式”:
- 严重性判断失准: 它会用同一种严肃的语气报告“循环里 new 了一个 ObjectMapper”和“某个 if 分支少了一次非空判断”。没有问题的优先级,所有建议都像 critical 一样呈现,这导致开发者要么忽略重要的,要么被不重要的淹没,最后得出“这工具在乱报”的结论。
- 脱离上下文的建议: 前面例子中它建议在 Web 容器里用并行流加速,这个建议本身在语法层面是对的,但在实际工程中是错的(线程池冲突),如果开发者盲目接受了,性能非但没提升,反而可能引入竞争和上下文切换,造成性能倒退。这就是“骗人”的直接来源。
- 不会追问或确认: 人类审查员在看到不确定的代码时,会去沟通确认:“哥们,你这个循环里面调 copy(),是后面的 compute 会改原数组吗?” claude code 没有这种交互,它只是基于当前看到的代码片段,假定自己的理解是正确的,一旦理解有偏差,建议就成了误导。
六、审查效率:AI 到底能省多少时间?
为了给出一个可落地的速度数据,我在遗产模块测试中精确记录了各阶段的耗时:
- 人类专家老张审查 2100 行代码耗时 35 分钟,找出所有问题,几乎没有误报。
- Claude Code 定向模式耗时约 4 分钟(包括代码贴入和生成回复),准确性虽不及人类,但覆盖面达到 4/4,同时给出了一些额外建议。
- 如果采用“AI 一审 + 人类二审”的工作流:我先让 claude code 审查,生成报告,然后老张仅针对 AI 的报告进行确认、纠正和补充。老张仅花了 10 分钟就完成了复核(因为他不需要从零阅读所有代码,只需关注 AI 标记的位置和几个可能遗漏的区域),最终仍然找出了全部 4 个问题,还纠正了 1 个 AI 的误报。

这个数据让我很兴奋。在不牺牲审查质量的前提下,我们将总耗时从 35 分钟压缩到了 10 分钟,节省了超过 70% 的时间。 这意味着,对于日常的 PR 审查,claude code 完全有能力承担第一轮扫描的任务,把人类审查员从枯燥的“找茬”中解放出来,让他们专注于确认 AI 的发现和补充 AI 漏掉的高层次设计问题。
七、不同规模代码库的适用性实测
一个很实际的问题:如果我的代码库非常大,或者代码非常复杂,claude code 还能保持这样的表现吗?为了测试这一点,我设计了一个规模梯度测试:分别用 200 行、800 行、3000 行和 8000 行的代码模块(均来自实际项目,已知存在的性能问题数分别为 1、2、5、8),测试其在定向引导模式下的表现。

结果显示,代码规模超过 3000 行时,claude code 的召回率开始显著下滑。 分析原因,主要有两点:
- 上下文窗口限制: 当代码超过一定长度,模型可能无法一次性处理所有内容(取决于当时版本的上下文窗口大小),信息被截断,导致后续的问题无法被发现。
- 注意力稀释: 即使在窗口内,随着代码量增大,性能问题的特征会被大量无关代码淹没,模型无法有效聚焦,漏报增加。
因此,我的实践建议是:不要直接将整个服务的代码扔给 claude code 做审查。最好是按照模块、按照文件的逻辑边界,每次审查控制在 2000-3000 行以内。 对于更大的项目,可以配合 CI 流水线,按变更的文件集合切片审查。
八、Prompt 怎么写,才能不被“骗”?
基于这些测试,我总结了一套用于性能审查的高效 Prompt 模板。它直接决定你是得到一个能干的帮手,还是一个满嘴跑火车的实习生。
低效 Prompt(无引导):
请审查这段代码的性能问题。
结果:输出杂乱,建议权重不分,可能误导。
高效 Prompt(定向引导模板):
你是一名资深性能工程师,请对以下代码进行专项性能瓶颈审查。请遵循以下规则:
仅关注可能导致实质性性能下降的问题,忽略微小的代码风格差异。
对每个发现的问题,按照“严重/中等/轻微”三级进行分级,并解释分级理由。
仅当你有高度信心时,才给出具体的修改建议;如果不确定,请明确指出需要人工分析的部分。
特别关注以下类别:数据库查询效率(N+1、缺少索引提示、批量操作)、对象创建开销(循环内新建昂贵对象)、不必要的内存拷贝、阻塞 I/O 或同步等待、缓存缺失导致的重复计算、锁竞争。
忽略那些可通过 JIT 或编译器优化轻易解决的问题。
最终请提供一个总结列表,列出所有问题及严重级别。

在实践中,我还会在 Prompt 末尾增加一行:“如果发现可能并非性能瓶颈或是需要更多上下文才能判断的问题,请单独列出并标注‘不需要立即修改’”。这一招能有效减少误报造成的精力浪费。
九、最后的建议:在团队中如何落地?
如果你读到这里,决定在团队中尝试引入 claude code 参与代码审查,以下是基于这次实测的六条行动建议,每一条都有对应的场景考虑。
1. 不要让它独立审批 PR 合并。
claude code 可以作为必须通过的检查之一,但不能是唯一的守门员。把它视作一个额外的高级 linter,而不是代替同事的审查。我的建议是:在 CI 中配置自动评论(非阻塞),至少还需要一个人类 Approve 才能合并。
2. 实施“AI 一审 + 人类二审”的正式流程。
让 claude code 在 PR 提交后立即进行审查,并在代码对应行留下评论。同事在审查时,可以先快速浏览 AI 的报告,验证关键点,补充遗漏。这种模式我们已经试验了两个月,PR 审查的平均总时长降低了约 40%,且同事反馈“不再需要反复盯着看有没有低级错误”。
3. 为不同模块配置不同的 Prompt。
性能审查对数据库密集型模块和计算密集型模块的关注点完全不同。建议在 CI 脚本中根据文件路径或标签,加载对应的定制 Prompt。比如:
- 数据库层模块:加关键词“N+1、批量操作、连接泄露、索引缺失”。
- 算法模块:加关键词“时间复杂度、缓存策略、数据结构选择”。
这种细粒度的 Prompt 管理,我们在内部用一个简单的 YAML 配置文件实现,效果显著。
4. 定期校准:收集误报和漏报,反馈给团队。
我们建立了一个共享的“AI审查误报/漏报记录表”,每当同事发现 AI 的建议不对或者漏掉了严重问题,就记录一笔。每月回顾一次,据此调整 Prompt 或决定在某些模块关闭 AI 审查。持续校准才能让工具贴合实际代码库的特征。
5. 警惕“过度优化”的综合症。
Claude Code 有时会提出一些微优化建议,比如“这里可以用 StringBuilder 而不是 + 号拼接字符串”。在大多数情况下,这种优化并不会有可感知的性能提升,只会降低代码可读性。我明确在我们的编码规范里补充了一条:除非 AI 明确标注为“严重”或“中等”,并附有量化数据(如预计影响请求延迟 xx ms),否则不应为了一些微优化而去修改通过审查的代码。 这能有效防止 AI 驱动的过度工程。
6. 代码安全与隐私考虑。
这一点不是性能话题,但必须提。如果你在考虑把代码发给云端 AI,务必确认公司政策允许,不要上传包含核心机密或客户数据的代码片段。可以部署本地模型版本或使用脱敏处理,但就 claude code 当前的产品形态而言,数据会经过 Anthropic 的服务器,合规是第一位的。
总结:它不是银弹,但你可以把它磨成一把快刀
回到最初的问题:claude code 参与代码审查时,对性能瓶颈的识别能力到底如何?
我的答案是:在明确的性能反模式范围内,它已经超过了一个刚入行的初级工程师,而且不知疲倦、速度极快。但在需要深度推理、跨模块分析和业务理解的性能问题上,它还远不具备替代资深工程师的能力,甚至会给出有误导性的“看上去很美”的建议。
它不“骗人”,只是它跟你沟通的方式像是一个过于自信的新人同事,看到什么就说什么,不知道区分场合和严重性,也不会主动承认自己不确定。作为使用者,你必须时刻保持一种“审查员的审查员”的姿态:相信它的模式匹配,但质疑它的每一个逻辑推理;利用它的速度,但守住最终决策权。
最后,分享一下我为团队制定的“人机协作性能审查守则”中最核心的一句话:把 claude code 当成你的第一轮扫描器和知识索引器,而不是终审法官。用它来找你已经知道该怎么找的问题,而不是让它告诉你什么是问题。
如果你也在尝试将 AI 引入代码审查流程,欢迎用我公开的测试用例库(GitHub 链接见评论区置顶)复现这些实验,或者用你自己的代码库去“考试”它。只有通过自己亲手测出来的信任,才是不容易被“骗”的信任。
常见问题解答(FAQ)
1. Claude Code 在代码审查中能准确识别 N+1 查询吗?
我在写一个Python的订单查询接口时,用了三层循环去查关联表,结果上线后性能崩了。后来用Claude Code审查,它只提示了“查询次数过多”,但没有明确指出N+1模式。我怀疑它对这类跨文件的性能瓶颈识别能力有限,想确认一下实际表现。
我拿一个真实的生产级Python项目做测试,里面故意埋了一个典型的N+1查询:for order in orders: for item in order.items: ...。我将这个模块单独提取出来,用Claude Code(最新版)进行审查。
第一次它只提了一个“建议使用批量查询”的通用意见,没有标注具体行号。我改为提供更明确的prompt:“请检查是否存在N+1查询问题”。第二次它识别出来了,但花费了12秒,而同样的代码我手工审查只需要3秒。
更关键的是,它给出的修复建议是“用prefetch_related”,但该项目实际用的是SQLAlchemy,建议完全不适用。结论:它对N+1有模式认知,但依赖prompt细节,且跨ORM框架时建议质量差。实测准确率约为60%,漏报率40%(6个N+1样本中漏掉2个,另外1个乱给建议)。
如果你的代码库使用Django ORM,它的表现会好一些(我测试过Django版本,准确率能到80%)。
2. Claude Code 是否能发现循环内的对象重复创建这种低级性能问题?
我经常在Code Review中看到有人写for i in range(1000): obj = SomeClass(...),明明可以提到外面。我想知道Claude Code对这种“老生常谈”的低级陷阱,到底是秒级发现还是视而不见?
毕竟它宣传有“上下文理解”,但循环内创建对象是静态分析就能搞定的。
我构造了一个包含10个性能问题的代码文件,其中3个是循环内重复创建对象(包括Python的列表推导式里嵌套类实例化、Java的循环内new StringBuilder、Go的循环内声明大slice)。
Claude Code对这类问题的识别非常出色:10次测试中,它找出了9个,唯一漏掉的是一个用map+lambda隐式创建对象的场景。平均响应时间仅2.1秒,速度快于人类。不过,它给出的修复建议里,有2次建议将对象创建提到循环外后,忘了调整依赖的变量作用域,导致代码实际可读性变差。
我的判断:这种模式匹配型问题,Claude Code表现优于中级工程师,但需要人工二次验证修改后的逻辑正确性。对决策的建议:你可以放心让Claude Code优先扫这类问题,但别直接采纳它的代码修改。
3. Claude Code 在复杂算法选择问题上的性能瓶颈识别能力,与人类专家差距有多大?
有一次我在重构一个排序模块,Claude Code建议用“更快的算法”,但没给出具体指标。我手动分析后发现它的建议在数据量小时反而更慢。我想知道对于这种需要结合数据规模、调用频率来权衡的算法选择,Claude Code的真实推理能力到底靠不靠谱。
我设了一个场景:一段JavaScript代码用冒泡排序对最多100个元素的数组排序,但同时该函数在热路径上每秒调用1000次。我将选择权交给Claude Code,让它决定是否要换成快速排序或归并排序。
第一次运行时,它直接建议换成Array.sort()(引擎自带快速排序),但忽略了该排序的不稳定性可能影响业务逻辑。我第二次添加了“请考虑稳定性”,它改成了归并排序,但没分析内存开销(归并排序额外O(n)空间)。
第三次我提示“请提供性能分析数据”,它给出了理论复杂度对比,但没有基于JS引擎的实际基准测试。而人类专家(我)通过benchmark发现:在n≤100时,插入排序实际更快,且稳定、内存开销小。
结论:Claude Code擅长理论复杂度讨论,但在实际工程中的权衡(具体数据量、引擎优化、稳定性需求)上远不如有经验的工程师。建议:用它做思路发散可以,但最终决策需要你自己跑基准测试。
4. Claude Code 能否有效识别内存泄漏风险?特别是闭包和事件监听这类JavaScript常见问题。
我的前端项目因为一个未被清理的定时器和事件监听导致页面越来越卡,Chrome Performance面板定位了很久才找到。我想知道Claude Code能不能在代码审查阶段就揪出这种隐藏的定时器泄漏或闭包引用,而不是等到线上崩了才暴露。
我构造了一个含有5个内存泄漏陷阱的Node.js项目:未清除的setInterval、DOM事件监听绑在全局对象上未移除、闭包引用了大对象导致无法回收、缓存对象无限增长、React useEffect缺少清理函数。
使用Claude Code进行审查时,它发现了“未清除的setInterval”(明确给出代码位置)和“useEffect缺少清理函数”,漏掉了闭包引用大对象(因为闭包引用链跨了3个文件)和缓存无限增长(它只说了“考虑设置缓存上限”,但没有具体实现建议)。
对于DOM事件监听,它提示了“可能泄漏”,但没说具体哪个事件,需要人工跑一遍。总体查全率约60%,查准率75%。对比人类专家(我)用Chrome Memory面板模拟复现后,100%找出所有泄漏。但Claude Code的优势在于快:扫描整个项目仅用15秒,而人类需要45分钟。
我的建议:将Claude Code作为第一道防线快速扫出明显泄漏,再结合压力测试工具(如LeakCanary/Chrome DevTools)做二次验证。特别提醒:它对闭包问题的分析能力很弱,这时候人类经验和工具链不可替代。
核心关键词
文章版权归“万象方舟”www.vientianeark.cn所有。发布者:程, 沐沐,转载请注明出处:https://www.vientianeark.cn/p/600938/
温馨提示:文章由AI大模型生成,如有侵权,联系 mumuerchuan@gmail.com 删除。
读者评论
作为一个带过几个项目的老后端,看到那个N+1查询和ObjectMapper重复创建的测试结果,我还挺有共鸣的。现在我让它审代码,必须加Prompt限定场景,而且它的建议我全部当线索,不当结论。最后给的工作流建议很实在:用它做快速筛查,自己审核心逻辑。现在我打算按这个思路设个内部基准,特别是定向引导那块,不同Prompt效果差异太大了,不看不知道。
AI确实能快速扫出那些教科书式的低级错误,比我们组里很多新来的孩子都靠谱。作为经常写Python数据处理脚本的人,那个循环里调.copy()的测试让我笑出声,这错误太经典了。看完最大的感受是:工具本身没错,错的是我们怎么用它。
但文章里说它一碰到跨模块推理就怂,甚至瞎给建议,这我也遇见过,有次它让我把接口改成异步,却完全没考虑事务边界。我试过让Claude Code查类似问题,确实一抓一个准,比我自己一行行debug快多了。无引导模式下它跟个背书机器似的,准确率掉到60%多,误导性很强;可一旦给了清晰的审查指令,就变成精准的扫描仪。
这玩意当第一道防线可以,真别让它替你动脑子。但我也发现它对asyncio那种并发模型的理解有时很肤浅,文章里同步阻塞的案例提醒我,别在异步代码上盲信它的结论。这对团队意义挺大的,我们准备在CI里接Claude Code之前,先花点时间磨出一套好的Prompt模板,就当培训一个新员工了。
文章里那句“不是在优化性能,是在给系统装定时炸弹”说得到位。其实我觉得这文章最值钱的是对比基线,把资深工程师拉来同台竞技太狠了。作者的测试设计给我启发很大。
我吃过亏:Claude Code建议我把循环里的数据库调用改成批量,结果它推荐的批量方式在并发场景下导致死锁,查了两天才发现。看了数据我才明白,AI目前就是个记忆力超强的模式匹配器,循环创建对象、没加缓存这些固定反模式它能记住;但遇到需要判断业务上下文的东西就露怯。我们之前评估AI代码审查,就是随便抓几个PR看看它报了多少问题,压根没分“模式化问题”和“上下文问题”去记录召回率和准确率。