拓冰建站拓冰建站
首页 / 资讯中心 / 正文

人机协同渐进式重构:用AI把“屎山”代码变成宝藏

1. 老代码库的困境我们是怎么走到“屎山”这一步的先说说背景。我接手这个项目的时候代码量大概在20万行左右Spring Boot后端加上Vue前端业务覆盖订单、库存、财务三条核心链路。系统不是什么大厂核心系统但属于那种“离了它业务就转不动”的遗留系统——上线五六年中间换过三拨开发最早的负责人早就联系不上了。代码是怎么变成“屎山”的说实话不是某一个人故意写烂而是长期缺乏秩序的自然结果。我刚进去的时候随手翻了一个订单查询接口280行的Controller方法里面有6层if嵌套、3处Thread.sleep、2个被注释掉的旧逻辑块、还有一段疑似复制粘贴后忘记改变量名的代码——它比较的是A订单的状态但操作的是B订单的数据。这种问题不是靠责任心能解决的因为写这段代码的人可能早就离职了没人知道当时的业务上下文。但“屎山”不是最可怕的最可怕的是它还能跑。20万行代码线上每分钟都在处理真实订单你敢随便动吗重构最大的障碍从来不是技术而是“不敢动、不知道动了会不会炸、炸了谁负责”。传统的大规模重写方案在这种场景下基本是找死——重写半年业务需求照常堆过来旧系统还得继续维护最后新旧两套系统并行维护成本翻倍团队心态崩溃。所以我们定了基调不重写、不推倒、不搞“百年大计”用渐进式重构逐步恢复秩序。而真正让这个方案能落地执行的关键是人机协同——用AI辅助做代码分析、拆解、生成、验证我们只做决策和把关。这篇文章就把整个实操过程梳理一遍包括方案选型、每个环节的具体做法、踩过的坑以及一些值得长期复用的经验。2. 人机协同重构的整体思路角色分工和推进节奏2.1 为什么选择“渐进式”而不是“推倒重来”先明确一个概念重构的目标不是“代码变好看”而是“在保证业务稳定交付的前提下逐步降低代码维护成本”。渐进式重构的核心逻辑是“化整为零”。把20万行代码按照业务模块拆开每个模块单独做重构做完一个上线一个上线后验证没问题再继续下一个模块。这样做有三个好处第一风险可控。每次改动只影响一个模块出问题能快速定位和回滚不会出现“改一处、炸一片”的连锁反应。第二收益前置。不是等半年后全部重构完才见效而是每两周就有一次交付团队的信心能保持住管理层也能看到持续进展。第三业务不阻塞。重构和日常需求开发并行新需求来了照常排期重构任务穿插在版本迭代中不会出现“重构期间业务全停”的极端情况。我见过太多团队死在一句话上“这个系统太烂了我们重写吧。”重写意味着什么意味着你要在旧系统还在运转的同时从零实现所有功能而这个过程中的所有业务变化都要同步到新系统里。六个月的周期算快的但六个月内业务需求的变化量可能比过去一年还多最后新系统上线的时候它可能已经落后于真实需求了。所以渐进式重构不是“没办法的办法”而是对于有真实业务压力的系统来说唯一靠谱的路径。2.2 AI在重构中的角色从“写代码”到“读代码”这轮重构最大的不同是我们引入了AI作为核心参与者。但我说的“人机协同”不是拿个AI工具让它自动生成一个新系统——那和推倒重写没有本质区别风险比人写还大。我理解的人机协同是把AI用在它最擅长的地方同时把人的经验用在AI不擅长的地方。AI在重构中适合做什么我总结了三件事代码阅读和理解。20万行代码没人能全部读完但AI可以。把整个模块的代码喂给AI它能快速梳理出调用关系、数据流向、异常分支甚至能发现一些人类忽略的隐藏依赖。重复性重构动作的批量执行。比如“把这个类里的所有公共方法提取成独立Service”“把这段重复的try-catch抽成统一异常处理器”——这种有明确模式的改造AI做起来又快又不会漏。测试用例的生成。重构的底气来自于回归测试而没有测试的老代码恰恰是最需要重构的。AI可以根据现有代码的行为自动生成覆盖主要流程的测试用例至少能兜住主线业务。AI不适合做什么第一不适合做业务判断。老代码里经常有一些“看起来是bug但实际是feature”的逻辑比如某个校验“看上去不合理”但去掉之后财务对账就会出问题。这种业务决策AI理解不了必须人来拍板。第二不适合做架构决策。系统该拆成几个微服务、哪些模块应该合并、用哪种设计模式去组织这些需要结合团队能力和业务规划去判断AI只能给参考意见不能替你做决定。所以我们的分工很明确AI负责“把代码看透、把模板活干完、把测试补齐”人负责“判断业务逻辑对不对、决定架构怎么调整、控制重构节奏”。2.3 整体节奏四个阶段滚动推进我们用了四个阶段做滚动推进而不是串行执行一遍就结束摸底调研确定模块边界梳理依赖关系识别高风险区。分析设计针对单个模块用AI做代码解析、建立行为模型、确定重构目标。改造迁移分步骤替换实现先抽取、后优化、再调整架构。验证复盘跑差异测试、人工code review、上线观察收集数据和反馈。为什么是“滚动”因为重构不是一次性的它是一个持续过程。第一个模块做完总结的经验会直接用到第二个模块上越做越快。到后期我们一个中等规模模块的重构周期从最初的三周压到了十天左右AI的作用也越来越明显。3. 摸底调研把20万行代码“看清楚”3.1 从“代码体检”开始摸底调研的第一步不是直接让AI读代码而是先做一次“代码体检”。我推荐的工具组合是用SonarQube做整体扫描拿到重复代码率、圈复杂度、代码异味数量这些硬指标用Structure 101或jdepend之类的工具分析包依赖关系找出循环依赖和“上帝类”手动跑一遍核心业务链路记录请求路径和时间消耗建立“线上真实行为”的感觉。SonarQube扫完之后我们拿到的数据触目惊心重复代码率17%、圈复杂度超过50的方法有89个、超过100的方法有6个、循环依赖3处、还有一堆不可达代码就是永远不会被执行的死分支。这些数据不光是给管理层看的“诊断报告”更重要的是帮我们自己确定优先级——圈复杂度高的方法、被大量类依赖的“上帝类”、循环依赖所在的包这些都是重构的优先攻坚对象。说个容易被忽略的点摸底阶段别只盯着代码看还要把当前线上运行的数据捞出来。哪些接口调用量最高哪些接口响应时间波动大哪些定时任务在跑这些信息决定了重构的排序。比如有一个看起来代码写得还不错的报表模块但每晚跑批都要两小时其它模块全都重构完了它的收益可能都没有——因为瓶颈不在代码质量上而在数据量上。这种模块就不该排进重构优先级。3.2 让AI“读一遍”代码建立全局认知代码体检之后我们把整个系统按业务域拆成若干个模块然后逐个模块丢给AI做深度解析。这里的“丢给AI”不是直接把代码库压缩包发过去让它看而是有技巧的。我们用的方案是先把模块的源码目录结构、核心类清单、数据库表结构导出来整理成提示词让AI输出一个“模块认知报告”——包括这个模块负责什么业务、核心实体有哪些、主要的调用入口和出口、存在哪些可疑逻辑。第二步针对AI输出的可疑点再让它做定点深入。比如AI说“订单模块的OrderServiceImpl类中有多处无用的Bean注入建议清理”这个判断本身已经很有价值了因为它意味着这个类可能有大量隐藏耦合。我们就会接着让它把这个类的所有依赖列出来标注出哪些依赖是真实使用的、哪些只是“注入但没有调用”的废弃依赖。这里有一个关键的心得AI分析代码的准确度和提示词的信息量强相关。如果你只是说“帮我分析OrderServiceImpl这个类”AI给的答案会比较泛但如果你把整个调用链路的上下文喂给它——谁在调它、它调了谁、它操作哪些表、它返回什么结构——它会给出很多非常具体的洞察。我做过对比同样的类详尽提示词和裸提示词的输出质量完全不在一个量级。3.3 建立“行为基线”重构前先搞清楚“它到底做了什么”摸底调研阶段的另一个核心产出是建立每个模块的“行为基线”。这个词是我在这次重构中反复强调的。什么叫行为基线就是在重构之前把当前系统的实际行为记录下来作为后续验证的参照物。比如某个接口重构前输入参数A、B、C返回值是什么、数据库产生了哪些变化、缓存key是什么、有什么异常分支这些细节全部记录成文档。为什么这件事这么重要因为老系统的很多行为特征光看代码是看不出来的。比如某个接口对入参的某个字段有“隐式容错”——传错了也不报错而是静默修正某个定时任务在极端情况下会重复执行但因为逻辑里有幂等处理所以没有出事某个缓存key的过期时间设置得极短导致频繁回源数据库但“歪打正着”保持了数据新鲜度。AI帮我们做的事是把这些“隐含行为”从代码里挖出来然后我们来判断哪些是业务需要的、哪些是历史遗留可以去掉的。这一步不做后面重构的时候一定会踩雷——你以为自己在“修复不合理逻辑”实际上是在“破坏现有的隐性契约”。我在摸底阶段还养成了一个习惯把代码里所有的TODO、FIXME、HACK注释全搜出来。别看这些注释不起眼它们是历届开发者留下的“活地图”。有一个模块的重构难点我们在代码里找了半天没找到核心逻辑最后发现入口处有一行注释“这里逻辑很绕见OrderServiceImpl.process()方法里的HACK临时处理订单重复提交后续需优化。”沿着这个线索我们才找到了那条处理重复提交的逻辑链。AI可以扫描代码结构但这种“人的蛛丝马迹”还是要靠人去追。4. 核心重构实操AI辅助下的三个典型场景4.1 场景一拆解350行的“上帝方法”我们重构的第一个攻坚目标是订单模块里一个350行的大方法。这个方法为什么能长到350行因为它把参数校验、权限判断、库存扣减、订单状态流转、日志记录、通知发送全揉在一起了就像一个流水线上只有一个人在干活他从收货到发货全包了。对于这种方法的拆解思路我总结为四步第一步用AI生成完整的“方法调用链图”——查清楚这个方法被哪些地方调用、它内部又调用了哪些私有方法、访问了哪些全局状态。第二步把方法内部的代码按“职责”切段每一段标注业务含义。这一步我们直接让AI做它按代码内容把350行分成了“参数校验”、“权限检查”、“库存预占”、“状态更新”、“异常补偿”、“日志与通知”六个独立段落。第三步人做决策哪些段落应该抽成独立方法抽出来之后放在哪个类新类的接口怎么设计这些是AI给不了答案的因为需要结合整个模块的架构方向来判断。第四步AI执行抽取并同步生成对应的单元测试用例。抽取的过程看起来简单但要特别注意“局部变量共享”的问题。这个方法里有几个局部变量在多个逻辑段之间传值比如一个从配置中心读取的库存阈值变量、一个保存了预占结果的状态标志。直接抽方法的话这些变量怎么传递会很别扭。我们最后的处理方案是先不急着抽全部而是分三步走——第一步把“日志与通知”这段不依赖中间状态的代码先抽出去第二步把“参数校验”抽成独立的工具方法第三步再处理真正有依赖关系的核心逻辑。这给了我一个很重要的教训重构不是一步到位而是可以分多轮、逐步下钻。每轮改动的范围越小验证的范围就越小出问题的概率就越低。AI能帮你看清全局但“怎么分步走”这个节奏还是要人根据自己的风险偏好来定。4.2 场景二干掉两组重复代码消除变更一致性隐患这个模块里有两组特别典型的重复代码。第一组是订单状态流转的逻辑散落在至少五个类里每个类的实现略有不同——有的多了状态校验有的少了。第二组是两个不同的Service类各自实现了一份金额计算逻辑结果有两个字段的小数位处理方式不一致导致财务报表偶尔出现“一分钱误差”。重复代码最大的问题不是“代码量浪费”而是“变更不一致”——你以为你改了一处实际上还有三处没改到系统行为就悄然发生了变化。这种问题的例子在金融系统里尤其多比如税率计算规则调整只改了A处没改B处月底对账的时候就出问题了。我们用AI处理重复代码的思路是先把所有相似的代码段全部扫出来然后让我们人工确认哪些是“真重复、可以合并”、哪些是“形似而神不似、动不得”。这个确认环节非常关键。比如那组状态流转逻辑粗看五个类都是一样的代码但仔细看下来其中两个类处理的订单类型不同一个是线上订单一个是线下补录订单两者的状态校验规则有细微差异。如果直接统一成一个方法就可能引入bug。所以AI帮我们做的是“找到相似的候选”但最终是否合并、合并后怎么兼容差异都需要人来判断。后来我总结了一套做法让AI输出“重复代码差异对比表”——每一组重复代码都列出文件位置、行数范围、相同点、差异点、疑似风险。人只需要逐条审核这张表做出“合并”或“不合并”的决定。这样审查效率极高也避免了一行一行去对比代码的机械劳动。4.3 场景三修复隐藏的并发bug顺手消除了幽灵订单有次AI在做代码解析时注意到一个被大量调用的库存扣减方法里有一行“不正经”的代码// 检查库存前先sleep一下防止并发问题 Thread.sleep(50);这行代码居然在生产环境跑了好几年。写它的人可能是遇到了并发下单导致超卖的问题不知道怎么解决就加了sleep来“碰运气”——每次扣库存前睡50毫秒用时间来错开并发请求。但这个方法被大量调用意味着每个订单请求都会额外增加50毫秒的延迟高峰期这个延迟的影响是很大的。我们用AI把这个方法的完整调用链梳理了一遍明确了并发冲突的根源是“检查库存”和“扣减库存”之间不是原子的。然后给出了一个干净的解决方案改成基于数据库行锁的原子操作在SQL层面用UPDATE ... WHERE stock ?的扣减方式影响行数为0则说明库存不足不需要SELECT UPDATE两步处理。-- 原子扣减库存 UPDATE inventory SET stock stock - #{quantity} WHERE product_id #{productId} AND stock #{quantity};这个改动让AI来完成模板代码的重写同时生成几个模拟并发场景的测试用例来验证。人工审核的时候我们特意用一个脚本并发发了50个订单请求确认最终库存数量和订单数量完全对得上——问题彻底解决了多线程sem抢占超卖的隐患消失了接口响应时间也平均降了40毫秒左右。这件事本身不算特别复杂但我觉得很有代表性老代码里很多“脏操作”其实是前人面对复杂问题的“土办法”AI能够帮你从全局视角找到这些土办法的根因然后给出更符合工程规范的替代方案。而人要做的是判断“这个土办法能不能动”——比如sleep这行代码看似简单但它可能是在某种特殊场景下的“最后一根救命稻草”贸然去掉前一定要想清楚。5. 用AI建立“重构安全网”测试生成与差异验证5.1 没有测试重构就是裸奔老代码重构最痛苦的事情就是没有测试。我们的系统里核心模块的测试覆盖率不到10%很多方法从来没有被测试过——倒不是开发不想写而是这些方法从诞生那天起就没人知道“正确输出是什么”。没有测试做安全网重构就是在裸奔。改一个逻辑你只能靠眼睛看代码“觉得没问题”但跑起来是不是真的没问题只能等上线后被用户发现。AI在这里帮了我们大忙——它能根据现有代码的行为反向生成“表征型测试用例”也就是把代码当前的输出当作“预期”。这种测试不算“正确性测试”但它能锁定当前行为防止重构后行为漂移。这不完美但在重构场景下这是性价比最高的兜底方案。举一个具体例子有一个订单汇总的私有方法输入是订单列表输出是一个汇总结果对象。我们用AI生成了一个包含正常订单、取消订单、退款订单、金额为零的订单、重复订单等候选场景的测试覆盖了空列表、单条记录、并发写入等边界情况。每次重构后跑一遍这组测试就能确认汇总逻辑没有发生变化。5.2 让AI写测试的提示词模板我用过很多次、觉得效果不错的测试生成提示词大概是这样的请根据以下Java方法生成单元测试用例JUnit 5。方法是 [粘贴方法签名和完整实现] 要求 1. 覆盖正常路径、边界条件和异常路径 2. 不改变当前行为对输入A期望输出为现有代码产生的结果 3. 每条测试的断言带上注释说明这个场景的业务含义 4. 如果发现方法中有不可达分支请标注出来并给出解释实际使用中AI生成的测试用例质量总体不错但需要人工做几件事检查测试断言是不是真的符合业务预期。AI生成“当前输出的快照”没问题但它不知道“这个输出对不对”。比如它可能把一个历史bug当成了“预期行为”写进断言这种测试反而会固化问题。所以每条断言都要人对一遍。补上业务上的关键场景。AI对业务不熟比如“订单取消后不能再次支付”这种规则你指望它自己想到不现实得由熟悉业务的人补充。控制测试粒度。AI比较容易生成“一个方法测一次、一次测一条路径”的测试但老代码通常有大量关联调用真正的风险在方法之间的交互。所以重点测试得放在集成层面而不只是单测。5.3 重构验证把新旧实现放到“小黑屋”里反复比较除了测试用例我们还有一种非常有效的验证手段——并行对比验证。简单来说就是把新旧两套实现同时跑起来输入同一份数据对比输出是否一致。这个思路可以用在接口层比如我们重构库存扣减逻辑的时候写了一个临时的对比脚本分别调用旧代码文件和新代码文件传入相同的请求参数比较响应结果、数据库变更和日志输出。任何不一致的地方都会进入待分析列表。这个方法对纯逻辑层的重构特别有效但对有副作用的外部调用比如发送短信、调用第三方支付不适用因为不能在测试环境真发短信。我们的处理方案是对这类外部依赖做Mock把新旧实现都跑在一个隔离的测试容器里外部调用全部打桩只比较内部逻辑的执行结果。有一个经验想特别分享对比验证阶段一定要刻意引入边界输入而不是只跑正常数据。比如订单状态流转的测试除了传正常的“待支付→已支付→已发货”还要传“已取消订单尝试支付”、“重复支付回执”、“金额为负数的退款请求”这种脏数据。很多老代码在边界输入下会表现出“奇怪的容错能力”——比如传负数金额不报错而是默默记0。这种“隐式容错”恰恰是最容易在重构中被破坏的。6. 从“屎山”到“宝藏”重构的长期收益和人机协同的心态转变6.1 重构带来的不仅是代码整洁更是团队能力的跃升重构过程中我们对核心模块做了大幅的瘦身订单模块的代码量从原来的3.5万行降到2.7万行圈复杂度超过50的方法从89个降到12个重复代码率从17%降到6%。不仅是代码指标变好看了更重要的是整个团队对这个老系统的理解深了不止一个层次。以前大家面对这个系统的时候心里想的是“能跑就行别乱动”。重构完之后大家的心态变成了“这里可以改、那里可以动”开发新需求的速度明显变快了。有个新来的同事说“这项目虽然是老系统但结构比我之前待的某些新项目还要清晰。”这句话让我挺欣慰的。代码质量提升的另一个直接好处是“事故率降了”。同样的模块重构前平均每个月有两三次因为并发问题或者“改了一处没改另一处”导致的小故障重构后的半年里这个数字降到了接近零。这也让我们有更多时间做真正的技术建设而不是每天救火。6.2 人机协同下程序员的价值在哪里这次重构让我对人机协同有了更深的理解。一开始团队里有人担心“AI会把我们的活干完吗”事实上AI的介入不仅没有让程序员“失业”反而让真正优秀的程序员更值钱了。原因在于AI把大量“机械工作”承担了之后人的精力可以集中在“真正需要人的判断力”的事情上。比如AI能告诉你这段代码的圈复杂度是87但它不能告诉你“为什么这个模块的业务如此复杂是因为缺少领域模型还是因为历史决策造成的”AI能生成一套测试用例但它不能告诉你“这个接口的调用方可能期待一个特殊格式的错误码”AI能写出优化后的代码但它不能替你做决定“这个隐藏的兼容逻辑是保留还是删除”。重构的本质不是“把代码写得更优雅”而是“让代码更好地表达业务”。而理解业务、做权衡、做决策这些永远是人的核心价值。AI是放大器放大的是人的判断力和决策力AI不是替代品它替代不了人的责任心和业务嗅觉。所以我给团队的建议是别怕用AI但别盲目信任AI。把AI当作一个“超级实习生”——活儿干得不少但每件事都得有人把关。这个把关的人才是项目真正的主人。6.3 给正在挣扎“屎山”的你三条建议最后整理三条我认为最值得分享的经验第一别追求一步到位的完美。老代码的“完美重构”是不存在的你追求的目标应该是“比昨天好一点、比上个月清晰一点、比去年可控一点”。每做一次小重构都是往“宝藏”的方向迈了一步。第二先建立安全网再做重构。很多人一上来就大刀阔斧地改结果改出了线上故障然后整个团队对重构都有阴影了。正确的顺序一定是先补测试、先做行为基线、先建立对比验证机制然后再动手改代码。安全网不会让你慢太多但它能保证你“摔不疼”。第三人机协同不是“把代码扔给AI”而是“把思考留给AI做第一轮把决策留给人做最后一轮”。AI帮你看到你可能看不到的东西但最终的系统怎么演进还是由人来做主。这次重构做下来我最深的体会是老代码不是“屎山”它更像是“矿山”——里面埋着历史业务的智慧、前人的经验和踩过的坑。而AI就是我们高效挖掘这座矿山的工具。工具是拿来用的不是拿来怕的。愿每一个跟老系统搏斗的人都能把身边的“屎山”挖成自己的“宝藏”。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门