AI 辅助研发内部复盘(2/5):老项目改造的工程化实践
摘要
生成式 AI 正在重塑软件工程的形态,但在面对沉积了数年甚至十数年的“老项目”(Legacy Code)时,大多数团队的尝试往往止步于简单的代码补全。老项目改造的真正难点,并不在于代码本身的复杂度,而在于代码之外的隐性知识、历史包袱以及错综复杂的业务耦合。本文将基于一线团队的实战复盘,系统性地剖析 AI 在老项目改造中的局限性,提出一套包含“九步法”的标准作业程序(SOP)与“三层人机分工模型”,并通过三个核心代码案例,展示如何将 AI 从“代码生成器”转化为可控的“工程加速器”。
第一章:老项目改造的认知陷阱——为什么 AI 总是“帮倒忙”?
在引入 AI 辅助工具(如 GitHub Copilot, Cursor, Claude Code 等)之初,团队普遍抱有极高的期待。然而,在老项目改造的实际场景中,我们很快遭遇了“理想与现实的落差”。
1.1 代码之外的“暗物质”
老项目的核心痛点通常不在明处的代码逻辑,而在暗处的隐性约定:
历史包袱:那些看起来“反模式”的代码,往往是为了兼容某个早已废弃的客户端版本,或是规避早期数据库的某个 Bug。AI 无法阅读 Git 提交记录背后的历史故事,自然会建议将其“优化”掉。
失传的知识:第三方系统的特殊对接方式、非标准的 API 鉴权逻辑、中间件版本的特定配置……这些信息大多只存在于老员工的脑海里,或散落在过时的 Wiki 中。
隐性边界:某些字段不能为空,某些状态机不能跳转,这些业务规则往往没有单元测试覆盖,仅靠代码注释或口口相传。
1.2 AI 的“幻觉”与“傲慢”
当 AI 面对上述“信息黑洞”时,它会基于训练数据中的“通用最佳实践”进行补全。这种“傲慢”在老项目中是致命的:
引入废弃依赖:AI 倾向于使用最新的库,而老项目可能锁定了特定的依赖版本。
破坏兼容逻辑:AI 可能会删除它认为“无用”的兼容代码,导致线上故障。
制造逻辑漏洞:在没有理解完整业务流程的情况下,AI 生成的代码可能通过编译,但在特定业务场景下会崩溃。
结论:在老项目中,“理解”的权重远高于“生成”。如果人脑没有先构建出完整的业务图景,AI 生成得越快,埋下的雷就越多。
第二章:方法论落地——老项目改造的“九步法”SOP
为了避免 AI 成为“埋雷机”,我们制定了一套标准化的老项目改造流程。该流程的核心思想是:前 70% 的时间用于理解,后 30% 的时间用于实施。
2.1 阶段一:全景扫描(步骤 1-4)
此阶段的目标是消除信息不对称,建立项目的全局认知。
找人沟通(最高优先级):寻找原开发者、产品经理或资深运维。问清楚:当初为什么要这么设计?有哪些坑?现在的业务重点是什么?这是获取隐性知识成本最低的方式。
阅读资料:系统性地查阅 README、Wiki、Jira 工单、设计文档。重点关注 Change Log 和故障复盘报告。
扫描代码:利用 IDE 或静态分析工具,快速识别技术栈、入口文件、模块边界以及核心链路。不要深究细节,先看森林,后看树木。
跑通环境:解决依赖安装、编译报错、数据库连接等问题。能成功启动服务并进行冒烟测试,是后续所有改造的物理基础。
2.2 阶段二:深度剖析(步骤 5-7)
此阶段要求工程师从宏观走向微观,精准定位改造靶心。
验证核心接口:从核心业务入口(如
Controller的 API)发起请求,观察数据流向。确认主流程是否通畅,日志输出是否符合预期。画关键图:这是将理解固化的关键一步。绘制架构图、ER 图、核心业务时序图。AI 可以在此阶段辅助生成图表草稿,但必须由人工审核修正。
确认影响范围:这是风险控制的核心。明确改动会波及哪些模块、接口和旧功能。评估兼容性成本,确定“哪些地方绝对不能动”。
2.3 阶段三:敏捷落地(步骤 8-9)
终于到了 AI 大展身手的时刻,但必须遵循“小步快跑”的原则。
小步改造:拒绝“一把梭哈”式的全局重构。将大模块拆分为独立的函数或服务,每次只修改一个小的逻辑单元。
分阶段验收:每一步改造后,立即进行回归测试和代码审查。建立快速的反馈闭环,避免最后时刻才发现大面积不兼容。
第三章:人机协作边界——三层分工模型
在老项目改造中,明确人与 AI 的职责边界至关重要。我们提出了“三层人机分工模型”,以最大化各自的优势。
3.1 AI 主导层:高效的“信息搬运工”
AI 擅长处理海量、琐碎、低认知负荷的任务。
职责:代码仓库扫描、正则表达式编写、文档摘要生成、简单的 CRUD 代码填充。
产出:接口清单、数据字典初稿、基础测试脚手架。
3.2 人机协作层:智慧的“探路者”
这是工程师与 AI 互动最频繁的区域,需要高度的智力参与。
职责:架构设计讨论、复杂算法实现、业务逻辑梳理。
交互模式:工程师提供上下文和约束,AI 提供实现思路和备选方案,工程师进行判断和选择。
产出:技术方案文档、核心算法代码、重构后的模块化结构。
3.3 人必须负责层:最终的“裁决者”
无论 AI 的能力多么强大,以下核心决策必须由人类工程师拍板,并对最终结果负全责。
定方向:判断业务目标和边界,决定技术选型。
排雷:识别第三方依赖风险、数据安全合规问题。
兜底:对代码质量、系统稳定性、交付进度负责。
第四章:代码案例实战——从理论到实践
以下三个代码案例展示了如何在上述方法论指导下,利用 AI 解决实际的老项目改造问题。
案例一:基于 AST 的“理解”层代码扫描
痛点:老 Java 项目中,MyBatis 的 Mapper 接口与 XML 文件分离,人工梳理 SQL 调用链极其耗时。
解法:利用 Python 的tree-sitter库解析代码,让 AI 辅助编写扫描逻辑,快速提取方法调用关系。
# scan_mapper_calls.py from tree_sitter import Language, Parser import os # 假设已编译好 java.so JAVA_LANGUAGE = Language('build/my-languages.so', 'java') parser = Parser() parser.set_language(JAVA_LANGUAGE) def find_mapper_calls(directory): """扫描目录下所有 Java 文件,查找 Mapper 接口调用""" call_graph = [] for root, _, files in os.walk(directory): for file in files: if file.endswith("Service.java"): path = os.path.join(root, file) with open(path, 'rb') as f: tree = parser.parse(f.read()) # AI 辅助编写的查询逻辑:查找所有方法调用节点 query = JAVA_LANGUAGE.query(""" (method_invocation object: (identifier) @mapper_name name: (identifier) @method_name arguments: (argument_list) @args ) @invocation """) captures = query.captures(tree.root_node) for node, tag in captures: if 'Mapper' in node.text.decode(): # 简单过滤 Mapper 调用 mapper = captures[0][0].text.decode() method = captures[1][0].text.decode() call_graph.append(f"{file} -> {mapper}.{method}") return call_graph # 输出示例: # UserService.java -> UserMapper.selectById # OrderService.java -> OrderMapper.insertSelective价值:将数天的手动梳理工作缩短至分钟级,为后续的影响范围评估提供了数据支撑。
案例二:基于规则文件的“约束”层代码生成
痛点:直接让 AI 修改老代码,风格不统一,且容易引入未授权的 API。
解法:创建CLAUDE.md(或.cursorrules),定义项目的“法律”,强制 AI 遵守。
# CLAUDE.md - 项目专属指令集 ## 项目背景 这是一个遗留的 Spring MVC 项目,JDK 8,禁止使用 Lambda 表达式(团队规范)。 ## 编码规范 1. **日志规范**:必须使用 `SLF4J`,禁止使用 `System.out.println`。 - 正确:`logger.info("Processing order: {}", orderId);` - 错误:`System.out.println("Processing order");` 2. **JSON 处理**:必须使用 `Fastjson`,禁止使用 `Jackson`。 3. **异常处理**:捕获异常后必须记录堆栈,禁止吞掉异常。 4. **SQL 限制**:禁止在循环内执行 SQL 查询(N+1 问题)。 ## 修改指令 - 当我要求修改代码时,请先列出受影响的文件。 - 代码风格必须符合阿里巴巴 Java 开发手册。 - 如果涉及事务,必须使用 `@Transactional(readOnly = false, rollbackFor = Exception.class)`。Prompt: "根据CLAUDE.md的规范,优化UserService.java中的getUserById方法,增加缓存逻辑。"
AI 行为:AI 会严格遵循上述规则,使用SLF4J打日志,使用Fastjson处理序列化,并正确添加事务注解。
价值:实现了 AI 生成的代码与现有项目规范的“无缝融合”,大幅降低了 Code Review 的成本。
案例三:基于语义对齐的“验证”层单元测试生成
痛点:老项目缺乏测试,且业务逻辑复杂,手工编写测试用例覆盖率低。
解法:利用 AI 将自然语言需求转化为可执行的测试代码,并进行反向验证。
// 原始老代码:复杂的折扣计算逻辑 public BigDecimal calculateDiscount(User user, BigDecimal amount) { // 逻辑:VIP3 满5000减300;VIP2 满2000打9折;新规则:VIP4 满10000免邮费并95折 if (user.getLevel() == 4 && amount.compareTo(new BigDecimal("10000")) >= 0) { // ... 复杂逻辑 ... } // ... 其他逻辑 ... } // AI 生成的测试代码(基于需求描述) import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; class DiscountServiceTest { @Test void testVip4Discount_ShouldApply95OffAndFreeShipping_WhenAmountOver10k() { // Arrange User vip4User = new User(4); BigDecimal amount = new BigDecimal("15000.00"); DiscountService service = new DiscountService(); // Act BigDecimal discountedAmount = service.calculateDiscount(vip4User, amount); // Assert // 验证 95折: 15000 * 0.05 = 750 assertEquals(new BigDecimal("14250.00"), discountedAmount); // 验证免邮费逻辑(假设通过另一个方法或副作用验证) assertTrue(service.isShippingFree()); } @Test void testVip4Discount_ShouldNotApply_WhenAmountBelow10k() { // ... 边界条件测试 ... } }价值:AI 不仅能生成测试代码,还能根据需求自动补充边界条件测试(如金额刚好等于 10000 的情况)。通过持续运行这些测试,我们建立了对老项目改造的“安全网”。
第五章:避坑指南与生存法则
在推进 AI 辅助改造的过程中,我们总结了两条必须警惕的极端倾向和五条实操红线。
5.1 警惕两个极端
过度依赖(“全自动挡”):认为 AI 无所不能,直接提交未经审查的代码。这会导致“幻觉”代码混入主干,引发生产事故。
过度保守(“完全不用”):因担心 AI 出错而拒绝使用,错失提效机会,导致团队在技术上逐渐落后。
正确姿势:培养“场景—工具—粒度—模型”的判断力。简单的重复性工作交给 AI(如生成 Getter/Setter),核心架构设计由人主导,复杂逻辑由人机协作完成。
5.2 五条实操红线(生存法则)
先理解,再下手:磨刀不误砍柴工。前期对业务和代码的理解投入,直接决定后期的改造质量。
能小改,不大改:优先采用绞杀者模式(Strangler Fig Pattern)或分支模式进行局部替换,避免全盘重写。
先框影响,再动代码:动手前必须在脑中或纸上推演一遍影响链条,明确改动波及范围。
快模型扫读,强模型决策:利用轻量级模型(如 GPT-3.5)进行代码阅读和摘要,利用重量级模型(如 Claude 3 Opus)进行复杂逻辑分析和代码生成。
人工验证速度必须跟上 AI 生成速度:如果 AI 一分钟生成 500 行代码,而人工需要一小时才能审查完,这就形成了风险积压。验证闭环的速度必须匹配生成速度。
结语
AI 辅助老项目改造,本质上是一次知识工程的实践。它不是简单的“代码翻译”,而是对遗留系统中隐性知识的挖掘、整理和显性化。在这个过程中,AI 是我们的“超级放大镜”和“高速打字机”,但握着方向盘、看着路况、决定目的地的,依然是人类工程师。
通过“九步法”SOP 和“三层分工模型”,我们可以将 AI 的不确定性纳入可控的工程流程之中。未来的软件工程师,核心竞争力将不再是编写代码的速度,而是定义问题、设计约束、验证结果的能力。让我们拥抱变化,从“码农”转型为真正的“AI 时代的软件工程师”。
免责声明:本文所述案例与方法论均基于特定团队的实践总结,仅供参考。在实际工程中应用,请务必结合您的具体业务场景、技术栈及安全合规要求进行充分评估与测试。
