我用 Claude Code 重写了公司内部审批流服务,省了3天,也踩了5个坑
一、接手这个"烂摊子"
事情的起因很简单:公司有一套跑了三年多的内部审批流后端服务,基于 Spring Boot 构建,负责处理员工请假、报销、采购等十几种审批场景。
系统能跑,但没人敢动。
接手之后我打开代码,第一感受是窒息——ApprovalController 单个文件将近 4000 行,ApprovalService 更夸张,超过 8000 行。整个文件从头到尾几乎没有一行注释,方法名是 doApprove、doApprove2、doApproveNew、doApproveV2Final 这种风格。没有任何单元测试,上线全靠手动点页面验。
我需要在两周内完成重构,同时保证线上服务不中断。
正常情况下光是读懂这堆代码就要花掉将近一周,更别说重写了。我决定把 Claude Code 拉进来——Anthropic 出的命令行 AI 编程工具,可以直接在终端里操作项目文件。
技术栈情况:
- 框架:Spring Boot 2.7 + MyBatis-Plus
- 数据库:MySQL 8.0
- 构建工具:Maven
- 代码规模:11 个核心模块,约 3.2 万行代码
二、Claude Code 真正能干活的地方
用下来,有三个场景它真的让我眼前一亮。
场景一:读懂旧代码,秒出注释和文档
第一步不是重写,是先搞清楚这堆代码到底在干什么。
我把 ApprovalService 里最核心的审批状态流转方法丢给 Claude Code,让它先生成 Javadoc 注释:
请为以下 Java 方法添加完整的 Javadoc 注释,
说明方法用途、每个参数的含义、返回值、
以及可能抛出的异常。不要修改任何业务逻辑。
[粘贴代码]
输出质量出乎意料地高。原来这个方法我读了二十分钟没完全搞明白,Claude Code 生成的注释把状态流转逻辑描述得清清楚楚。当然有两个参数的描述有点偏,但整体八成以上可以直接用。
改写前(无注释):
public Boolean doApprove(Long id, Integer status, String remark, Long operatorId) {
ApprovalRecord record = approvalMapper.selectById(id);
if (record == null || record.getStatus() != 1) return false;
record.setStatus(status);
record.setRemark(remark);
record.setOperatorId(operatorId);
record.setUpdateTime(new Date());
approvalMapper.updateById(record);
if (status == 2) {
notifyService.sendApproved(record);
} else if (status == 3) {
notifyService.sendRejected(record);
}
return true;
}
Claude Code 生成注释后:
/**
* 执行审批操作,更新审批记录状态并触发对应通知。
*
* <p>仅当审批记录处于「待审批」状态(status=1)时,操作才会生效。
* 审批通过(status=2)将触发审批通过通知;
* 审批拒绝(status=3)将触发审批拒绝通知。</p>
*
* @param id 审批记录唯一ID
* @param status 目标状态(2-通过,3-拒绝)
* @param remark 审批备注,允许为空
* @param operatorId 执行审批操作的用户ID
* @return 操作是否成功;记录不存在或状态不为待审批时返回 false
*/
public Boolean doApprove(Long id, Integer status, String remark, Long operatorId) {
// ... 原有逻辑不变
}
这一步帮我节省了大量"读代码猜意图"的时间,整个模块的文档注释覆盖,一天不到全部完成。
场景二:重复性重构,十几个 if-else 变策略模式
这套审批系统支持十几种审批类型,原来的实现方式是一个巨大的 if-else 链:
if ("LEAVE".equals(type)) {
// 请假审批逻辑,约200行
} else if ("EXPENSE".equals(type)) {
// 报销审批逻辑,约180行
} else if ("PURCHASE".equals(type)) {
// 采购审批逻辑,约230行
}
// ... 还有十几个分支
整个 processApproval 方法超过 1500 行。
让 Claude Code 把它重构为策略模式,它给出的结构非常清晰:
// 策略接口
public interface ApprovalStrategy {
String getType();
ApprovalResult process(ApprovalContext context);
}
// 各审批类型实现类(Claude Code 自动生成骨架)
@Component
public class LeaveApprovalStrategy implements ApprovalStrategy {
@Override
public String getType() { return "LEAVE"; }
@Override
public ApprovalResult process(ApprovalContext context) {
// 原有请假逻辑迁移至此
}
}
// Context 统一调度
@Service
public class ApprovalStrategyContext {
private final Map<String, ApprovalStrategy> strategyMap;
public ApprovalStrategyContext(List<ApprovalStrategy> strategies) {
this.strategyMap = strategies.stream()
.collect(Collectors.toMap(ApprovalStrategy::getType, s -> s));
}
public ApprovalResult process(String type, ApprovalContext context) {
ApprovalStrategy strategy = strategyMap.get(type);
if (strategy == null) throw new UnsupportedApprovalTypeException(type);
return strategy.process(context);
}
}
原来 1500 行的怪物方法,拆成了 14 个职责单一的策略类。每个类单独维护,新增审批类型只需加一个实现,不再动核心逻辑。
场景三:单元测试从 0 到 60%
重构之前,这个项目的单元测试覆盖率是 0%。让 Claude Code 针对核心 Service 方法生成 JUnit5 + Mockito 用例:
@ExtendWith(MockitoExtension.class)
class ApprovalServiceTest {
@Mock
private ApprovalMapper approvalMapper;
@Mock
private NotifyService notifyService;
@InjectMocks
private ApprovalService approvalService;
@Test
void doApprove_shouldReturnFalse_whenRecordNotFound() {
when(approvalMapper.selectById(anyLong())).thenReturn(null);
Boolean result = approvalService.doApprove(1L, 2, "ok", 100L);
assertFalse(result);
verify(notifyService, never()).sendApproved(any());
}
@Test
void doApprove_shouldNotify_whenApproved() {
ApprovalRecord record = new ApprovalRecord();
record.setStatus(1);
when(approvalMapper.selectById(1L)).thenReturn(record);
approvalService.doApprove(1L, 2, "approved", 100L);
verify(notifyService).sendApproved(record);
}
}
两天内,核心模块测试覆盖率从 0% 提升到约 62%。
三、踩坑实录:Claude Code 让我栽跟头的 5 件事
当然不是什么都顺利。这 5 个坑,希望能帮你少走弯路。
坑1:它引用了一个不存在的方法
重构 ApprovalRecord 的工具类时,Claude Code 生成了这样一行:
String normalizedRemark = StringUtils.trimAllWhitespaceAndNormalize(remark);
我当时以为是 Spring 的 StringUtils 里的方法,版本可能不对,查了半天 Maven 依赖,升了一次 Spring 版本,还是报 NoSuchMethodError。
最后去翻源码才发现:这个方法根本不存在。是 Claude Code 自己造出来的,名字听起来非常像官方 API,但实际上从未存在过。
教训: 所有 AI 生成的外部方法调用,必须逐一到源码或官方文档里核实。“看起来像"不等于"确实有”。
坑2:上下文一长就开始"断片"
当我把整个 ApprovalService(约 8000 行)一次性丢给它处理时,它在重构后半段把前面已经定义好的 ApprovalContext 类给"忘了",开始用 ApprovalDTO、ApprovalRequest 等各种不一致的命名,生成的代码根本无法编译。
教训: 长文件必须拆开处理,每次只投喂一个方法或一个类。我后来的习惯是:每次上下文不超过 300 行,效果稳定很多。
坑3:业务逻辑它根本不懂
审批系统里有一段会签逻辑:多个审批人必须全部通过,审批才能流转到下一节点;但如果某个审批人的级别达到 L5 以上,可以直接一票通过。
Claude Code 生成的代码把这两个条件的判断顺序写反了——先判断了全员通过,再判断 L5 特权,导致 L5 审批人一票通过的逻辑永远不会触发。
代码读起来完全没问题,逻辑也很流畅,但业务跑起来就是不对。这个 bug 在测试环节才被发现,险些上线。
教训: 凡是和具体业务规则强绑定的核心逻辑,不能直接用 AI 生成结果,只能让它提供框架,逻辑本身必须自己写或逐行审查。
坑4:测试用例只测"快乐路径"
Claude Code 自动生成的测试用例,几乎清一色是正常输入、正常返回的场景。
边界条件——比如审批人 ID 为 null、审批记录已被其他人操作导致的并发冲突、会签场景下中途有人撤回——基本没有覆盖。
这些场景我后来自己补了将近 40 个测试用例。
教训: AI 生成测试用例之后,必须自己列出边界清单逐一补充。把异常场景描述清楚告诉它,它也能生成,但得你主动去问。
坑5:有一段代码我没看懂,却通过了 Review
Claude Code 用了一段比较巧妙的 CompletableFuture 链来优化并行通知逻辑。代码写得很优雅,同事 Review 时也说"没问题",我觉得"应该没问题"就合入了。
一周后,压测时发现在高并发场景下这段逻辑存在线程池耗尽的风险。回头再细看,才发现它用的是默认的 ForkJoinPool,没有做任何线程池隔离。
教训: 如果你自己看不懂 AI 生成的代码,就不应该合入。理解是你的责任,不是 AI 的。
四、我总结的人机协作分工边界
用下来,我大概总结出了这张表:
| 任务类型 | 建议做法 | 原因 |
|---|---|---|
| 添加注释 / 生成文档 | ✅ 完全交给 AI | 不涉及逻辑,风险低,省时间 |
| 重复性重构(消除重复代码) | ✅ 完全交给 AI | 有固定模式,AI 擅长 |
| 样板代码(DTO、Builder、测试框架) | ✅ 完全交给 AI | 规律性强,几乎无需修改 |
| 单元测试用例 | ⚠️ AI 生成 + 人工补边界 | 正常路径 OK,边界需人工补充 |
| 外部 API 调用 / 第三方依赖 | ⚠️ 必须人工验证 | 幻觉风险高,容易引用不存在的方法 |
| 核心业务逻辑 | ❌ 只做参考,不直接使用 | AI 不理解业务上下文 |
| 架构决策 | ❌ 不要交给 AI | 需要全局视野和业务判断 |
| 并发 / 安全相关代码 | ❌ 必须人工实现 | 风险太高,不容出错 |
五、最终结果
重构耗时从预估的两周缩短到约 9 天,其中 Claude Code 帮我节省了大约 3 天的时间,主要集中在注释生成、样板代码和测试用例初稿上。
核心数据:
- 代码行数:从 3.2 万行压缩到约 1.9 万行,删除了大量重复逻辑
- 单元测试覆盖率:从 0% 提升到约 62%
- 上线后首两周线上 Bug 数:比历史同期减少了约 60%
六、个人判断
Claude Code 确实让我提效了,但它没有让我变成一个更好的程序员——那部分还得靠自己。
它更像一个很快、不知疲倦、但偶尔会一本正经说错话的实习生。你得知道什么能交给他做、做完要检查哪里,而不是直接合并他的 PR。
AI 编程工具的价值,在于把你从重复性劳动里解放出来,让你把精力放在真正需要判断力的地方。但判断力本身,还是你自己的事。
最后问一句:你们在用 AI 编程工具重构项目时,遇到过什么更离谱的坑?评论区见。
本文为个人原创实战记录,首发于 CSDN。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)