用AI维护十年老项目:从摸底到迭代的完整方法论
十年老项目最大的敌人不是技术债,是上下文丢失。AI最大的价值不是帮你写代码,是帮你重建上下文。从项目体检到文档沉淀,完整拆解七步法。
十年老项目最大的敌人不是技术债,是上下文丢失。AI 最大的价值不是帮你写代码,是帮你重建上下文。
写在前面
每个开发者都遇到过这种情况:接手一个老项目,代码量巨大、文档缺失、原始开发者早已离职。打开文件夹,几十万行代码,完全不知道从哪里下手。
更常见的情况是:这个项目还在跑,还有用户在用,还需要加新功能。你不能重写,不能停服,只能在一辆行驶中的火车上换轮子。
核心不是"让 AI 直接改代码",而是先让 AI 建立地图、控制风险、小步改动、自动验证。
最稳的流程是:
1. 项目体检
→ 2. 建立项目地图
→ 3. 梳理业务规则
→ 4. 补测试保护网
→ 5. 小范围改动
→ 6. 自动化回归
→ 7. 文档沉淀
这篇文章把每个环节拆开讲透。
一、先别急着改,让 AI 读懂项目
接手老项目的第一周,不要改任何代码。先让 AI 帮你做"项目体检"。
1.1 让 AI 回答这几个问题
- 项目技术栈是什么
- 启动命令是什么
- 目录结构含义
- 核心业务模块有哪些
- 数据库表关系
- 外部接口依赖
- 最危险的历史代码区域
具体操作:
# 查看依赖
cat package.json | jq '.dependencies, .devDependencies'
# 或者 Python 项目
pip freeze
# 或者 Java 项目
cat pom.xml
# 按语言统计代码量
cloc src/
# 最近 50 次提交
git log --oneline -50
# 按文件统计改动频率
git log --pretty=format: --name-only | sort | uniq -c | sort -rg | head -20
1.2 让 AI 输出项目地图
让 AI 把以上信息汇总成一个文件:
# PROJECT_MAP.md — 老项目结构地图
## 技术栈
Java / Spring Boot / Vue / MySQL / Redis
## 核心模块
- 用户登录、订单、支付、权限、报表
## 高风险区域
- 支付回调(涉及资金,出错即事故)
- 权限校验(绕过即安全漏洞)
- 老接口兼容(改了老客户端会崩)
- 定时任务(出问题不会立即发现)
## 依赖风险
- Spring Boot 2.1.x(已 EOL,有已知 CVE)
- 某个内部 SDK 锁死在 1.0.3
## 建议改造优先级
1. 支付模块(风险最高,测试覆盖为零)
2. 订单模块(改动频率最高)
3. 用户模块(依赖最多)
这一步非常关键。没有项目地图,AI 改老项目很容易乱改。
1.3 画模块关系图
让 AI 读入口文件、路由配置、核心模块,输出模块依赖关系:
请分析这个项目的模块调用关系,输出:
1. 入口接口/路由
2. 核心服务调用链
3. 涉及的数据库表
4. 外部依赖(第三方 API、消息队列等)
5. 模块之间的依赖方向
你不需要画得多漂亮,关键是搞清楚:
- 模块之间的调用关系
- 数据流向
- 哪些是核心模块(被依赖最多),哪些是边缘模块
二、让 AI 先做"解释型任务",不要马上写代码
2.1 让 AI 做调用链分析
拿到新需求后,不要马上让 AI 写代码。先让它分析影响范围。
比如拿到一个"修改订单状态"的需求:
请只分析这个订单模块的调用链,不要修改代码。
输出:
1. 入口接口
2. 调用服务
3. 涉及数据库表
4. 可能影响的功能
5. 风险点
2.2 老项目最怕的三件事
改一个字段,炸三个页面;
改一个接口,影响老客户端;
改一个 SQL,报表数据全变。
所以 AI 第一阶段应该负责:读代码、画关系、找风险。
2.3 让 AI 做风险评估
根据这个需求,评估改动风险:
1. 哪些现有功能可能受影响
2. 哪些接口的调用方需要同步修改
3. 数据库变更是否需要迁移
4. 是否有兼容性风险
5. 建议的灰度策略
三、先补测试,再加新功能
十年老项目通常测试很少,甚至没有。这时不要追求一次补全,先补"保护性测试"。
3.1 测试优先级
核心接口冒烟测试 —— 保证基本能跑
关键业务回归测试 —— 保证核心流程不出错
数据库查询结果校验 —— 保证数据一致性
权限边界测试 —— 保证安全
老功能快照测试 —— 保证行为不漂移
3.2 按需求补测试
比如要新增优惠券功能,先保护这些:
- 下单流程
- 价格计算
- 退款流程
- 订单状态流转
让 AI 做:
根据现有订单逻辑,生成最小回归测试清单。
要求只覆盖高风险路径,不要追求全面。
3.3 AI 生成测试的正确姿势
第一步: 让 AI 读代码,生成测试用例初稿。
第二步: 你来审核。AI 生成的测试往往偏"正确但无聊"——覆盖了明显场景,但缺乏业务特殊性的理解。你需要补充的是:
- 这个功能的用户是谁?他们最可能怎么用?
- 什么情况下用户会骂人?
- 有没有已知的线上 bug 需要覆盖?
第三步: 跑一遍,看哪些挂了。挂了的测试往往是隐含的业务规则——代码的行为和你以为的不一样。这些就是你丢失的上下文。
3.4 用覆盖率找盲区
# Vitest
vitest run --coverage
# Jest
jest --coverage
覆盖率低的模块就是你的盲区——要么不重要,要么很重要但你不知道。
关键原则:先补测试,再改代码。测试通过 = 当前行为被记录下来了。
3.5 进阶:业务复杂时,AI 如何保证测试覆盖率?
当项目迭代多年、业务逻辑错综复杂时,一个常见的思路是"把历史需求和代码整理成知识库,喂给 AI 生成用例"。但这会遇到两个致命问题:
问题一:上下文爆炸。 全部历史需求、代码、Bug 单塞进 Prompt,轻松超过模型窗口限制(几百 K 到 1M token)。
问题二:拆太碎联动性丢失。 按模块拆分知识库可以缓解窗口问题,但模块之间的关联——比如"改了订单模块会影响库存模块"——就被切断了。
这不是"整理知识库"的问题,而是信息检索 + 精准注入的问题。最优解是三层架构:
第一层:建知识库(一次投入,长期复用)
历史需求文档 ──┐
历史测试用例 ──┤
历史 Bug 单 ──┼──→ 向量化索引(RAG)
代码仓库 ──┤
数据库 Schema ──┤
定时任务配置 ──┘
关键点:不是把所有东西塞进 Prompt,而是建索引,按需检索。
第二层:新需求进来时,智能检索相关上下文
新需求描述
↓
意图识别 → 这个需求涉及哪些模块?
↓
从知识库中检索:
- 相关模块的历史用例(最近 3 个版本的)
- 相关代码的变更历史
- 相关的历史 Bug 单(同模块高频问题)
- 相关的数据库表结构
↓
只注入 Top-K 相关片段(控制在模型窗口内)
↓
AI 生成用例
Token 消耗从"全量"变成"精准切片",降低 10 倍以上。
第三层:代码变更影响分析(替代人工梳理)
这是让 AI "逆推需求"的正确打开方式:
# 伪代码思路
# 1. 从 Git 获取当前迭代的代码 diff
git_diff = get_diff(current_sprint)
# 2. AI 分析 diff 影响的模块和接口
impact = ai_analyze("""
以下是本次迭代的代码变更:
{git_diff}
请分析:
1. 涉及哪些业务模块?
2. 影响了哪些 API 接口?
3. 涉及哪些数据库表?
4. 与哪些历史功能有关联?
""")
# 3. 根据影响分析,从知识库精准检索
relevant_cases = rag_search(
query=impact,
filters={
"modules": impact.affected_modules,
"version_range": "last_3_versions",
"bug_frequency": "high"
}
)
# 4. 注入精准上下文,生成用例
test_cases = ai_generate(
current_requirement=new_requirement,
related_history=relevant_cases,
code_context=impact.code_changes,
max_tokens=8000 # 控制窗口
)
方案对比:
| 方案 | Token 消耗 | 覆盖率 | 联动性 | 落地难度 |
|---|---|---|---|---|
| 全量塞知识库 | 极高 | 高 | 强 | 低 |
| 分 Skill 模块 | 中 | 中 | 差 | 中 |
| MCP 逐个查 | 高 | 高 | 强 | 中 |
| RAG + 影响分析 + 精准注入 | 低 | 高 | 强 | 中高 |
大厂内部降低 Token 消耗 3-4 倍的方案,大概率就是这个方向的变体——用代码变更做影响分析,只检索相关切片,而不是全量灌入。
落地建议:
- RAG 基础: 学一个向量数据库(Chroma / Pinecone),把历史用例和 Bug 单向量化
- 代码分析: 用 AST 解析或 Git diff + LLM 分析影响范围
- 编排层: 用 Claude Code / Agent 做编排,串联"需求 → 影响分析 → 检索 → 生成"
- 验证闭环: 生成的用例人工标注好坏,反馈回 RAG 提升检索质量
这比"整理历史需求为知识库"高了一个层次——不是整理,而是自动检索和精准注入。
四、新功能要用"外挂式改法"
老项目最推荐的改法不是大重构,而是:
尽量新增,不轻易修改老逻辑。
4.1 两种改法对比
比如要加一个"会员折扣":
不推荐:
直接改原来的 priceService 核心逻辑
推荐:
新增 MemberDiscountService
在原价格计算流程后增加一个可开关的折扣步骤
也就是:
老逻辑不动
新逻辑旁路
配置开关控制
灰度发布
出问题可关闭
这是维护老项目最现实的方式。
4.2 Strangler Fig 模式(绞杀者模式)
这是处理老项目最经典的策略:
旧代码不动
↓
新功能用新架构写
↓
逐步把旧功能的流量切到新代码
↓
旧代码最终被替代
AI 的角色:
- 帮你读旧代码的业务逻辑,翻译成新架构的实现
- 对比新旧实现的输出是否一致
- 生成适配层(Adapter)让新旧系统共存
关键:不要试图一次性重写。 新旧并行运行一段时间,确认新代码没问题后再下掉旧代码。
4.3 选择先改哪个模块
不要:重写整个项目
要:选一个模块 → 补测试 → 重构 → 验证 → 下一个
选择标准:
- 业务价值高的模块优先——用户高频使用的功能,改好了立刻有收益
- 改动频率高的模块优先——频繁改动说明痛点明显,重构后提效最大
- 依赖少的模块优先——风险低,适合练手,建立信心
五、AI 改代码必须限制范围
5.1 给 AI 加边界
不要这样说:
帮我实现会员系统
要这样说:
只修改以下 3 个文件:
- UserController.java
- MemberService.java
- member_mapper.xml
不要修改数据库结构。
不要影响原登录逻辑。
输出修改点和风险说明。
给 AI 加边界,才能减少乱改。
5.2 每次改动都要求 AI 输出影响面
标准模板:
## 本次改动
新增会员等级字段展示
## 修改文件
- UserDTO.java、UserController.java、user.vue
## 影响功能
- 用户详情页、后台用户列表
## 风险点
- 老接口字段兼容
- 前端空值展示
## 回归测试
- 老用户无会员等级时页面正常
- 新用户有会员等级时展示正常
这个比单纯"代码写好了"重要得多。
5.3 AI 辅助 Code Review
让 AI review 代码,重点关注:
- 安全漏洞(注入、XSS、权限绕过)
- 边界条件(空值、超长输入、并发)
- 性能问题(N+1 查询、内存泄漏)
- 与现有代码的一致性
但不要完全信任 AI 的 review。 AI 可能会漏掉业务逻辑层面的问题,这些只有了解业务的人才能发现。
六、自动化回归
6.1 工具选择
每次改动后必须跑测试。工具优先级:
| 优先级 | 工具 | 适用场景 | Token 消耗 |
|---|---|---|---|
| 1 | Playwright CLI | 日常浏览器自测 | 低 |
| 2 | Vitest / Jest | 单元测试和集成测试 | 中 |
| 3 | Playwright MCP | 深度页面探索 | 高 |
6.2 推荐的验证流程
# 开发时
tsc --noEmit && pnpm lint # 0 token,纯本地
# 提交前
vitest run --reporter=json # 极低 token,只读 summary
# 功能验证
playwright-cli open → snapshot # 低 token,存磁盘
# 接口验证
curl ... | jq '.status' # 0 token,纯 shell
# 上线前
next build && npm audit # 极低 token
核心原则:能本地跑的别让模型跑,能存文件的别塞上下文,能一行命令解决的别写测试脚本。
6.3 智能回归选择
如果项目有几千条测试用例,全跑一遍要 2 小时。AI 可以根据这次代码改了什么,只跑相关的测试用例,20 分钟搞定。
这是 ROI 最高的 AI 测试能力之一。
七、建立 AI 维护文档体系
每次让 AI 改功能,都先读这些文档。上下文会越来越稳定,不用每次重新解释。
7.1 建议的文档结构
/docs
├─ PROJECT_MAP.md 项目地图
├─ MODULE_RULES.md 业务规则
├─ API_MAP.md 接口清单
├─ DB_SCHEMA.md 数据库说明
├─ RISK_AREAS.md 高风险模块
├─ REGRESSION_LIST.md 回归测试清单
└─ AI_CHANGE_LOG.md AI 改动记录
7.2 每个文档的作用
PROJECT_MAP.md — 技术栈、模块架构、依赖关系。项目体检的产物。
MODULE_RULES.md — 每个模块的业务规则。比如"订单超过30天自动关闭"、"VIP用户享受9折"。
API_MAP.md — 接口清单,包括入参、出参、调用方。防止改了接口不知道谁在用。
DB_SCHEMA.md — 数据库表关系说明。防止改了字段不知道谁在查。
RISK_AREAS.md — 高风险模块清单。每次改动前必看。
REGRESSION_LIST.md — 回归测试清单。每次改动后必跑。
AI_CHANGE_LOG.md — AI 每次改动的记录,包括改了什么、影响什么、风险点。防止重复犯错。
7.3 文档跟着代码走
只改代码不改文档,下次 AI 读到的上下文还是错的。
每次改动完成后,同步更新相关文档。AI 可以帮你做这件事——让 AI 根据代码变更自动更新文档。
八、最推荐的人机协作方式
8.1 分工明确
人负责:判断业务对不对、决定改动范围、审核风险、最终上线
AI 负责:读代码、找调用链、生成测试点、写局部代码、补文档、分析报错
8.2 标准开发流程
需求进来
↓
AI 读相关模块代码,输出影响分析
↓
AI 生成实现方案(2-3 个选项 + 权衡)
↓
你选方案,AI 写代码(限定文件范围)
↓
自动跑测试(playwright-cli + vitest)
↓
AI 输出改动报告(修改文件/影响功能/风险点)
↓
你审核,确认上线
8.3 三个最容易踩的坑
坑一:上来就重构。 没有测试保护就重构,改一个崩三个。先补测试,再动代码。
坑二:全量重写。 十年的 edge case 你重写不完。老项目里那些"奇怪"的代码,可能是在绕一个你不知道的线上 bug。渐进式改造,不要推倒重来。
坑三:AI 说"这里可以优化"就改。 AI 看到"烂代码"会建议你优化。但那个"烂代码"可能是在绕一个线上 bug,或者兼容某个已下线的第三方接口。改之前先搞清楚为什么这么写。
九、从明天开始的行动清单
第一周:摸底
- 让 AI 扫描技术栈和依赖
- 生成代码规模统计
- 输出模块架构图(PROJECT_MAP.md)
- 分析 Git 历史
- 标记高风险区域(RISK_AREAS.md)
第二周:补测试
- 识别核心业务逻辑(P0)
- 让 AI 生成测试用例初稿
- 人工审核和补充
- 跑一遍,记录失败用例
- 建立回归测试清单(REGRESSION_LIST.md)
第三周:首次迭代
- 选一个改动频率高的模块
- 让 AI 做影响分析
- 用"外挂式改法"小范围改动
- 跑测试验证
- 确认没问题后合并
- 更新 AI_CHANGE_LOG.md
第四周:建立日常流程
- 固化 AI 辅助开发的标准流程
- 建立代码 review 清单
- 配置 CI/CD 自动化测试
- 补充业务规则文档(MODULE_RULES.md)
- 复盘前三周的经验
写在最后
十年老项目不是负担,是资产。它跑了很多年,说明它解决了真实的问题。你要做的不是推倒重来,而是让它跑得更好。
AI 在这个过程中的角色是辅助理解,不是替代思考。它可以帮你快速扫描代码、生成测试、发现模式,但最终的判断——哪些能改、哪些不能改、优先级怎么排——还是要靠人。
一句话总结:
十年老项目不能让 AI 当"主刀医生",要让 AI 当"资深助手":先拍片、再标风险、补保护网、最后小刀口改动。
本文是"肖恩的博客"系列文章之一,首发于 seanwalter.top。作者是一名从软件测试转型AI领域的开发者,记录在转型过程中的真实思考。