上周,我做了一件有点羞耻的事。
把自己写了4年的一个核心服务,让AI做了一次完整的Code Review。
结果AI给我列了一张清单。
我看了大概10分钟,关掉了文档,去倒了杯水,回来,删掉了30%的代码。
不是因为代码有bug。
功能一直是正常的,线上跑了4年,没出过任何问题。
是因为AI让我看到了一些东西,我自己已经看不见了。
一、为什么我要做这件事
这个服务是我4年前搭的,核心是处理支付和费用计算的逻辑。
刚写的时候,我很认真。注释清楚,结构分明,自认为写得很好。
然后它就这样跑了4年。
偶尔加个功能,改个字段,但整体结构没人动过。因为"能跑",因为"没空",因为"改了万一出问题"。
上个月,产品提了一个新需求,要在这个服务里加一个费用分摊的逻辑。我打开代码,看了半个小时,发现自己有点看不进去了。
不是代码太复杂,而是太熟悉了。
每一行我都认识,但我已经不记得它们为什么这么写了。
那一刻我意识到:我对这份代码的感情,已经妨碍了我客观地评估它。
于是我做了一个决定:把它交给AI看一遍。
二、我怎么让AI做Review
很多人让AI review代码,用的是这种Prompt:
帮我review一下这段代码
然后AI给你一大段泛泛的夸奖加一两条无关痛痒的建议。
没用。
我用的是一个拆分了5个维度的Prompt模板:
请对以下代码做Code Review,按照这5个维度逐一分析:
1. 潜在bug(空指针、并发风险、边界条件)
2. 安全风险(注入漏洞、越权、敏感信息泄露)
3. 性能问题(N+1查询、大对象、不必要的重复计算)
4. 可维护性(类职责是否单一、耦合度、扩展性)
5. 代码质量(命名、注释、复杂度)
对每个问题:
- 指出具体的类名和行号
- 说明为什么是问题
- 给出具体的修复建议
- 标注严重程度:严重 / 中 / 低
[粘贴代码]
维度拆分是关键。
不加维度约束,AI会给你一个"综合感受";加了维度,AI会逐项审查,漏掉问题的概率大幅降低。
我把核心的3个类,大概1200行代码,分3次喂给AI,每次一个类。
然后我把AI的清单整理出来,开始逐条看。
三、AI发现了什么
发现1:一个存在4年的潜在NPE
AI标注的第一个问题,严重程度:严重。
java
// FeeCalculationService.java 第43行
public BigDecimal calculateFee(Order order) {
return order.getItems().stream()
.map(item -> item.getPrice().multiply(
new BigDecimal(item.getQuantity())))
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
AI的分析:
order.getItems() 未做null检查。若订单明细为空列表,stream正常返回ZERO,但若getItems()本身返回null,将在.stream()处抛出NullPointerException。 同理,item.getPrice() 若为null,.multiply()将直接抛出NullPointerException。 建议在方法入口处添加参数校验,或使用Optional包装可能为null的返回值。
我看完,第一反应是:这个方法跑了4年没出过问题啊。
但我知道AI说得对。
正常业务数据不会有null,但如果有一天做数据迁移,或者清理历史数据,或者某个上游服务异常返回了空对象------这个方法就会爆。
4年没出问题,不代表不是问题。只是运气好。
这是我自己看不见的那种问题。不是因为我技术不行,而是因为我太熟悉这段代码了,每次看到它,脑子会自动补全"这里没问题"。
修复很简单,两行校验的事。但发现它,需要一双没有先入之见的眼睛。
发现2:一个我以为很优雅的设计,其实是自我感动
这个发现让我沉默了更久。
AI标注的第二个问题,严重程度:中。
PaymentStrategyFactory.java -- 工厂类
PaymentStrategy.java -- 策略接口
WechatPaymentStrategy.java -- 微信实现
AlipaymentStrategy.java -- 支付宝实现
AbstractPaymentTemplate.java -- 模板抽象类
PaymentContext.java -- 上下文类
共6个类,合计约460行
AI的分析:
当前系统中支付方式共2种,模板方法模式、策略模式和工厂模式三层叠加, 引入了约340行的抽象代码。这些扩展点在过去4年内从未被使用过。 建议评估是否真的需要这一层抽象。若近期无新增支付方式的计划, 可以简化为一个PaymentService类加if-else判断,约100行, 等真正需要扩展时再重构,代码的可读性和维护成本都会更低。
我坐在那里,盯着这段分析看了很久。
这6个类是4年前我刚学完《设计模式》之后写的。
当时我觉得自己写得很漂亮。策略模式、工厂模式、模板方法,全用上了,还互相配合,简直教科书级别。
写完之后还给同事讲解过,他们说:"哇,这个设计很完整。"
但AI说的是真相:这不是优雅,这是用复杂度证明自己懂设计模式。
4年过去了,从来没有第三种支付方式出现过,从来没有人动过那3个抽象层,那340行代码唯一的作用就是让新来的同事读代码时多花半个小时。
我删掉了这5个类,合并成了1个PaymentService,120行。
功能完全一致,单元测试全部通过。
删完之后,心里有点空,又有点轻松。
发现3:一个真实的安全漏洞
这个是我最后悔的。
AI标注的第三个问题,严重程度:严重。
java
// ReportQueryService.java 第112行
public List<FeeRecord> queryByCondition(QueryRequest request) {
String sql = "SELECT * FROM fee_record WHERE merchant_id = "
+ request.getMerchantId()
+ " AND status = '" + request.getStatus() + "'";
return jdbcTemplate.query(sql, new FeeRecordRowMapper());
}
AI的分析:
request.getStatus() 直接拼接进SQL,存在SQL注入风险。 若用户传入 ' OR '1'='1 这样的参数,可以绕过status条件查询所有记录。 建议改用参数化查询:
javaString sql = "SELECT * FROM fee_record WHERE merchant_id = ? AND status = ?"; return jdbcTemplate.query(sql, new FeeRecordRowMapper(), request.getMerchantId(), request.getStatus());
这个方法,是3年前一个下午赶时间写的。
当时用的是MyBatis-Plus,这个方法是个例外,临时用的jdbcTemplate,一直没人改。
功能上没问题,所以没人注意到。
但这是一个真实存在的SQL注入漏洞,在生产环境跑了3年。
修复是5分钟的事。
但发现它的,是AI,不是我。
发现4:一个AI说错了的建议(这个很重要)
AI的建议不全是对的。
在分析性能问题时,AI给出了这样一条:
RiskControlService.getRiskLevel() 在每次计算费用时都会调用, 建议加入本地缓存(如Caffeine),设置5分钟过期, 可以减少约80%的重复调用,提升性能。
这个建议,我没有采纳。
因为这个接口返回的是实时风控等级,由风控团队根据用户行为动态更新。
5分钟的缓存,意味着风控规则的变更最多延迟5分钟生效。在我们的业务场景里,这5分钟的延迟可能导致资损。
这条上下文,在代码里没有注释,在文档里也没有记录,只存在于我和风控团队两年前的一次对接会议里。
AI不知道。它只看到了"重复调用",看不到"为什么必须实时"。
这是AI做Review必然存在的盲区:它理解代码,但不理解代码背后的业务决策。
四、删掉30%之后
把AI的清单过了一遍,修复了能修复的,拒绝了不合适的,最终结果:
修改前:1240行
修改后:860行
删减比例:31%
修复严重问题:2处(NPE风险 + SQL注入)
删除过度设计:340行(支付策略三层抽象)
清理冗余注释和死代码:约40行
单元测试:全部通过
功能:完全一致
代码变少了,但我对它的信心变高了。
然后我问自己一个问题:为什么这些问题,我自己4年都没有发现?
答案是3个字:太熟了。
熟悉带来了3种盲区:
第一种,熟悉度遮蔽。 自己写的代码,大脑会自动补全"应该没问题"的判断,看代码时其实是在"回忆",而不是在"审查"。NPE那个问题我看过这段代码上百次,每次都跳过了。
第二种,沉没成本。 那6个类花了我好几天时间,还讲解给同事看过,删掉等于承认它们是多余的。人不愿意承认自己做了无用功,会本能地为它辩护。
第三种,没有外部压力。 能跑、没bug、没投诉,就没有动力主动审视。所有人都在向前跑,没人回头看。
AI没有这3种包袱。
它不认识我,不知道这段代码我写了多久,不知道我当年对那个设计有多得意。
它只看代码本身。
这是它作为Review者的最大优势。
五、3个实操建议
做完这次Review,我想给你3个具体的建议。
第一,用分维度的Prompt,不要用泛问。
不要说"帮我review这段代码",而是明确列出你想检查的维度:bug、安全、性能、可维护性。维度越清晰,AI给出的问题越具体,越有价值。
完整的Prompt模板,在文章末尾,回复「review」获取。
第二,定期给老代码做一次AI Review,不只是新代码。
新代码你还有印象,自己review相对有效。老代码才是盲区的重灾区------能跑、没人动、谁都不想碰,恰恰是问题最容易藏身的地方。
建议每季度挑一个"能跑但没人动"的模块,专门做一次AI Review。
第三,AI的建议要过滤,重点找它不懂的地方。
AI不知道你们公司的业务规则,不知道某个"看起来可以优化"的调用背后是否有特殊约束。它给的建议里,有10-20%可能不适合你的场景。
过滤本身也是有价值的------它逼你重新想清楚"这段代码为什么这么写"。
如果你能回答这个问题,很好,在注释里写下来。
如果你回答不上来,那可能真的值得改了。
写在最后
12年写代码,我以为我很了解自己的代码。
AI用20分钟告诉我:你只是习惯了它,不等于你真的看清楚了它。
人会对自己写的代码产生感情。
AI不会。
这是它的局限,也是它的价值。
回过头来看,那个SQL注入漏洞让我反思了很久。
它在生产跑了3年。
不是我不懂SQL注入,不是我不知道参数化查询。
是因为那天下午很赶,写完跑通就提交了,后来再也没有人仔细看过它。
这样的代码,在每个人的项目里都有。
你有多久没有认真看一眼自己写的老代码了?
你有没有试过让AI review自己的代码?发现了什么?欢迎评论区聊聊。
后端AI实验室 不讲概念,只谈实战 代码开源,每周更新