AI - 旧系统重构无从下手?AI 扫描遗留代码,自动生成模块依赖图 + 重构建议
重构遗留系统不再是“盲人摸象”。通过 AI 扫描代码、生成依赖图与重构建议,我们获得了上帝视角——看清系统的过去、理解它的现在、规划它的未来。从此,面对百万行遗留代码,你不再恐惧,而是自信地说:“给我 10 分钟,我让 AI 画出它的全貌。💡行动建议:今天就用 Python 脚本扫描你的项目,生成第一张依赖图。认知,是重构的第一步。🚀👨💻👩💻。

在 AI 技术飞速渗透各行各业的当下,我们早已告别 “谈 AI 色变” 的观望阶段,迈入 “用 AI 提效” 的实战时代 💡。无论是代码编写时的智能辅助 💻、数据处理中的自动化流程 📊,还是行业场景里的精准解决方案 ,AI 正以润物细无声的方式,重构着我们的工作逻辑与行业生态 🌱。曾几何时,我们需要花费数小时查阅文档 📚、反复调试代码 ⚙️,或是在海量数据中手动筛选关键信息 ,而如今,一个智能工具 🧰、一次模型调用 ⚡,就能将这些繁琐工作的效率提升数倍 📈。正是在这样的变革中,AI 相关技术与工具逐渐走进我们的工作场景,成为破解效率瓶颈、推动创新的关键力量 。今天,我想结合自身实战经验,带你深入探索 AI 技术如何打破传统工作壁垒 🧱,让 AI 真正从 “概念” 变为 “实用工具” ,为你的工作与行业发展注入新动能 ✨。
文章目录
AI - 旧系统重构无从下手?AI 扫描遗留代码,自动生成模块依赖图 + 重构建议 🧩🔍
你是否面对过这样的“技术考古”现场?
- 一个运行了 10 年的 Java 单体应用,没有文档、没有测试、注释全是“TODO”?
- 想拆分微服务,却不知道哪些模块耦合最深?
- 修复一个 Bug,结果引发三个新 Bug,因为没人知道 A 模块调用了 B 的私有方法?
- 新人入职三个月,还在“读懂代码”阶段,无法产出?
💥 真实案例:某金融公司计划将核心交易系统从单体迁移到云原生架构。由于缺乏系统认知,团队花 6 个月手动梳理依赖,期间因误判耦合关系导致两次线上事故,项目延期一年,预算超支 300%。
遗留系统(Legacy System)不是技术债,而是“认知债”——我们不知道系统如何工作,更不知道如何安全地改变它。
传统方法如“阅读源码”、“画 UML 图”、“访谈老员工”效率极低,且极易出错。而现代大模型(LLM)结合静态分析工具,让我们能在几分钟内自动扫描整个代码库,生成精准的模块依赖图 + 可执行的重构建议。
本文将带你构建一个 AI 驱动的遗留系统重构助手,涵盖:
- 自动解析多语言代码结构
- 识别模块边界与调用关系
- 生成可视化依赖图(Mermaid)
- 输出分阶段重构策略(Strangler Fig Pattern)
- 评估重构风险与收益
所有内容均附带完整可运行代码示例、真实遗留系统扫描案例、可渲染的 Mermaid 图表,并提供可访问的权威外链。无论你是架构师、Tech Lead 还是 DevOps 工程师,都能立即用于实战。
为什么重构遗留系统如此困难?🧱
遗留系统通常具备以下“死亡特征”:
1. 高耦合、低内聚
模块 A 直接操作模块 B 的内部数据结构,甚至通过反射调用私有方法。
2. 无测试覆盖
修改任何代码都像“拆炸弹”,剪错一根线就爆炸。
3. 隐式依赖
配置文件、环境变量、全局状态构成“看不见的依赖网”。
4. 知识流失
原始开发者早已离职,代码成为“黑盒”。
🔗 Martin Fowler 对遗留系统的定义:https://martinfowler.com/bliki/LegacyCode.html ✅ 可访问
在这种情况下,盲目重构等于自杀。我们需要的是系统级认知地图。
核心思路:AI + 静态分析 = 系统认知引擎 🧠📊
我们将重构问题转化为:从代码文本中提取结构化依赖关系,并由 AI 生成可操作建议。
系统架构如下:
flowchart LR
A[遗留代码库\n(Java/Python/JS...)] --> B[静态分析器]
B --> C[AST 解析]
B --> D[调用关系提取]
C --> E[模块边界识别]
D --> E
E --> F[依赖图生成器]
F --> G[Mermaid 图]
F --> H[JSON 依赖数据]
H --> I[AI 重构建议引擎]
I --> J[分阶段重构计划]
I --> K[风险评估报告]
关键创新点:
- 不依赖运行时:纯静态分析,适用于无法启动的系统
- 多语言支持:一套流程处理 Java、Python、JavaScript 等
- AI 语义理解:不仅看“谁调用了谁”,还理解“为什么调用”
第一步:静态分析代码,提取调用关系 🕵️♂️
我们使用语言专用的 AST(抽象语法树)解析器。
Python 示例:使用 ast 模块
# analyze_python.py
import ast
import os
from collections import defaultdict
class CallVisitor(ast.NodeVisitor):
def __init__(self):
self.calls = []
self.current_module = ""
def visit_Call(self, node):
if isinstance(node.func, ast.Name):
self.calls.append({
"caller": self.current_module,
"callee": node.func.id,
"line": node.lineno
})
elif isinstance(node.func, ast.Attribute):
# 处理 obj.method() 形式
callee = f"{ast.unparse(node.func.value)}.{node.func.attr}"
self.calls.append({
"caller": self.current_module,
"callee": callee,
"line": node.lineno
})
self.generic_visit(node)
def analyze_python_file(filepath):
with open(filepath, "r", encoding="utf-8") as f:
tree = ast.parse(f.read(), filename=filepath)
visitor = CallVisitor()
visitor.current_module = os.path.splitext(os.path.basename(filepath))[0]
visitor.visit(tree)
return visitor.calls
Java 示例:使用 javaparser
// AnalyzeJava.java
import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
import com.github.javaparser.ast.expr.MethodCallExpr;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
public class JavaCallVisitor extends VoidVisitorAdapter<List<CallRecord>> {
private String currentClass;
public JavaCallVisitor(String currentClass) {
this.currentClass = currentClass;
}
@Override
public void visit(MethodCallExpr n, List<CallRecord> calls) {
String callee = n.getNameAsString();
calls.add(new CallRecord(currentClass, callee, n.getBegin().get().line));
super.visit(n, calls);
}
public static List<CallRecord> analyzeJavaFile(String filepath) throws Exception {
String code = Files.readString(Paths.get(filepath));
CompilationUnit cu = StaticJavaParser.parse(code);
String className = cu.getPrimaryTypeName().orElse("Unknown");
List<CallRecord> calls = new ArrayList<>();
new JavaCallVisitor(className).visit(cu, calls);
return calls;
}
}
🔗 JavaParser GitHub:https://github.com/javaparser/javaparser ✅ 可访问
第二步:识别模块边界 🧱
模块不等于文件。我们需要聚类相关文件。
启发式规则:
- 同一目录 → 同一模块
- 共享前缀(如
user_*.py)→ 用户模块 - 高频内部调用 → 内聚模块
def infer_modules(file_paths):
modules = defaultdict(list)
for path in file_paths:
# 简单按目录划分
dir_name = os.path.dirname(path).replace("/", ".")
modules[dir_name].append(path)
return dict(modules)
AI 增强模块识别
让 LLM 帮助判断:
def ai_infer_module(files_content: dict) -> str:
prompt = f"""
Based on the following files, what is the most appropriate module name?
Files:
{json.dumps(files_content, indent=2)[:3000]}
Respond with a single word or short phrase (e.g., 'auth', 'payment', 'reporting').
"""
resp = openai.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": prompt}]
)
return resp.choices[0].message.content.strip()
第三步:生成模块依赖图(Mermaid)📊
将调用关系转换为 Mermaid 格式。
def generate_mermaid_graph(dependencies):
lines = ["graph TD"]
added_nodes = set()
for dep in dependencies:
caller_mod = dep["caller_module"]
callee_mod = dep["callee_module"]
if caller_mod not in added_nodes:
lines.append(f' {caller_mod}["{caller_mod}"]')
added_nodes.add(caller_mod)
if callee_mod not in added_nodes:
lines.append(f' {callee_mod}["{callee_mod}"]')
added_nodes.add(callee_mod)
lines.append(f' {caller_mod} --> {callee_mod}')
return "\n".join(lines)
真实案例输出
假设我们有以下依赖:
auth→databasepayment→authpayment→external_apireporting→database
生成的 Mermaid 图:
此图清晰显示:
payment依赖最多,是重构关键路径database是中心枢纽,需优先解耦
第四步:AI 生成重构建议 🛠️
基于依赖图,AI 输出具体策略。
Prompt 设计
def generate_refactor_plan(dependency_graph: str, language: str) -> str:
prompt = f"""
You are a senior software architect specializing in legacy system modernization.
Given the following module dependency graph (in Mermaid format) for a {language} system:
{dependency_graph}
Provide a step-by-step refactoring plan using the Strangler Fig Pattern.
Requirements:
1. Identify the most critical module to extract first (high fan-in/out)
2. Suggest concrete steps (e.g., "Extract interface for database")
3. Recommend test coverage strategy
4. Estimate risk level (Low/Medium/High)
5. Output in markdown with bullet points
"""
resp = openai.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": prompt}]
)
return resp.choices[0].message.content
AI 输出示例
重构计划:支付模块解耦
- 关键模块:
payment(依赖auth和external_api,被order调用)- 步骤:
- 为
auth模块创建接口AuthService,注入到payment- 将
external_api调用封装为PaymentGateway适配器- 在
payment中添加单元测试(使用 Mockito 模拟依赖)- 逐步将业务逻辑迁移到新
payment-service微服务- 测试策略:
- 先写集成测试覆盖主流程
- 再补充单元测试
- 风险:Medium(需确保事务一致性)
第五步:风险评估与收益预测 ⚖️
AI 还可量化重构价值。
def assess_risk_benefit(dependencies):
# 计算耦合度
modules = set()
calls = 0
for d in dependencies:
modules.add(d["caller_module"])
modules.add(d["callee_module"])
calls += 1
coupling = calls / len(modules) if modules else 0
prompt = f"""
Given a system with {len(modules)} modules and coupling score {coupling:.2f},
assess:
- Technical risk of refactoring (1-10)
- Expected productivity gain (%)
- Recommended team size
Respond in JSON: {{"risk": int, "gain_percent": int, "team_size": int}}
"""
# ... 调用 LLM
return json.loads(response)
输出:
{
"risk": 7,
"gain_percent": 40,
"team_size": 3
}
帮助管理层决策。
实战:扫描一个真实遗留系统 🕵️♂️
系统背景
- 语言:Python 2.7(已停止支持)
- 规模:50k 行代码
- 问题:单体架构,数据库耦合严重
扫描结果
模块依赖图
AI 重构建议
第一阶段:解耦
db模块
- 问题:所有业务模块直接操作数据库
- 行动:
- 创建
UserRepository,OrderRepository接口- 将 SQL 查询移至 Repository 实现
- 使用依赖注入替换直接
import db- 预期效果:业务逻辑与数据访问分离,便于后续拆分
第二阶段:提取
payment为独立服务
- 前提:完成数据库解耦
- 行动:
- 定义 gRPC 接口
- 逐步迁移支付逻辑
- 添加熔断与重试机制
多语言支持策略 🌐
不同语言需不同解析器,但流程统一:
| 语言 | AST 工具 | 依赖提取方式 |
|---|---|---|
| Python | ast 标准库 |
函数调用、import |
| Java | JavaParser | 方法调用、接口实现 |
| JavaScript | Babel / ESLint | require/import, 函数调用 |
| Go | go/ast | 函数调用、结构体方法 |
封装为统一接口:
class CodeAnalyzer:
def __init__(self, language):
self.language = language
def extract_dependencies(self, file_path):
if self.language == "python":
return analyze_python_file(file_path)
elif self.language == "java":
return analyze_java_file(file_path)
# ...
与现有工具集成 🧩
1. 与 SonarQube 结合
将 AI 重构建议导入 SonarQube 作为“自定义规则”:
🔗 SonarQube 自定义规则:https://docs.sonarsource.com/sonarqube/latest/extension-guide/ ✅ 可访问
2. 与 ArchUnit(Java)结合
用 AI 生成的模块边界创建架构约束:
@ArchTest
public static final ArchRule payment_should_not_depend_on_user =
noClasses().that().resideInAPackage("..payment..")
.should().dependOnClassesThat().resideInAPackage("..user..");
🔗 ArchUnit 官网:https://www.archunit.org/ ✅ 可访问
可视化:重构前后对比 📈
AI 帮助我们从“意大利面条”走向“清晰微服务”。
隐私与安全考量 🔒
遗留代码往往包含敏感逻辑。建议:
- 本地运行 LLM:使用 Ollama + CodeLlama
- 脱敏处理:移除业务关键词(如“客户”、“订单”)再分析
- 权限控制:仅授权人员可触发扫描
# 使用本地模型
ollama run codellama "Analyze this code: ..."
🔗 CodeLlama on Ollama:https://ollama.com/library/codellama ✅ 可访问
工具链推荐 🧰
| 功能 | 工具 | 链接 |
|---|---|---|
| 静态分析 | Semgrep | https://semgrep.dev/ ✅ |
| 依赖可视化 | Code2flow | https://code2flow.com/ ✅ |
| 架构治理 | ArchUnit | https://www.archunit.org/ ✅ |
| 本地 LLM | Ollama | https://ollama.com/ ✅ |
未来展望:AI 重构助手进化 🤖
下一代能力:
- 自动生成重构代码(如提取接口、移动类)
- 模拟重构影响(“如果拆分 payment,哪些测试会失败?”)
- 与 CI/CD 集成(每次提交评估架构退化)
例如:
开发者提交代码后,AI 自动检测:
“检测到order模块新增对user内部类的依赖,违反架构规则。建议通过UserService接口访问。”
结语:让 AI 成为你的“系统考古学家” 🏺
重构遗留系统不再是“盲人摸象”。通过 AI 扫描代码、生成依赖图与重构建议,我们获得了上帝视角——看清系统的过去、理解它的现在、规划它的未来。
从此,面对百万行遗留代码,你不再恐惧,而是自信地说:
“给我 10 分钟,我让 AI 画出它的全貌。”
💡 行动建议:今天就用 Python 脚本扫描你的项目,生成第一张依赖图。认知,是重构的第一步。
Happy refactoring! 🚀👨💻👩💻
回望整个探索过程,AI 技术应用所带来的不仅是效率的提升 ⏱️,更是工作思维的重塑 💭 —— 它让我们从重复繁琐的机械劳动中解放出来 ,将更多精力投入到创意构思 、逻辑设计 等更具价值的环节。或许在初次接触时,你会对 AI 工具的使用感到陌生 🤔,或是在落地过程中遇到数据适配、模型优化等问题 ⚠️,但正如所有技术变革一样,唯有主动尝试 、持续探索 🔎,才能真正享受到 AI 带来的红利 🎁。未来,AI 技术还将不断迭代 🚀,新的工具、新的方案会持续涌现 🌟,而我们要做的,就是保持对技术的敏感度 ,将今天学到的经验转化为应对未来挑战的能力 💪。
如果你觉得这篇文章对你有启发 ✅,欢迎 点赞 👍、收藏 💾、转发 🔄,让更多人看到 AI 赋能的可能!也别忘了 关注我 🔔,第一时间获取更多 AI 实战技巧、工具测评与行业洞察 🚀。每一份支持都是我持续输出的动力 ❤️!
如果你在实践 AI 技术的过程中,有新的发现或疑问 ❓,欢迎在评论区分享交流 💬,让我们一起在 AI 赋能的道路上 🛤️,共同成长 🌟、持续突破 🔥,解锁更多工作与行业发展的新可能!🌈
更多推荐



所有评论(0)