每个老项目都是一座没插路标的森林。这周我开始执行自己的"只管去写"计划,Day2的任务很明确:从本地仓库里挑一个尘封多年的旧系统,用 Claude 把它的技术栈彻底摸一遍底。这个项目是一位已经离职的同事留下的,没有设计文档,没有接口说明,连根README都是五年之前写的。面对这样一座"代码债矿山",我决定先不急着写任何功能,而是让 Claude 先当一次侦察兵,把整个项目从入口到出口的技术脉络全部拉出来。
选择 Claude 而不是自己硬啃代码,不是因为懒,而是因为这类老项目有个通病:你永远不知道哪些文件是真在用的,哪些是当年实验失败的尸体。人工翻目录容易陷入"局部最优"——看到某个熟悉的框架就觉得懂了,实际上整个系统的组装逻辑藏在 config 和脚本里。让模型先从全局视角做一次粗扫,我再针对可疑点深挖,效率会高很多。这篇文章就记录一下我用 Claude 分析老项目技术栈的完整过程、提示词设计思路,以及几处差点被忽略的坑。
1. 接手陌生老项目的第一个问题:你的入口在哪里
1.1 先别急着问"这是什么",先问"文件怎么组织"
很多人在分析老项目时会犯一个错误:上来就让 AI "介绍一下这个项目"。这种问法得到的回答往往泛泛而谈,比如"这是一个基于XX的管理系统",毫无价值。因为大型代码库里真正决定架构的信息,不在单个文件里,而在文件之间的组织关系里。
我让 Claude 做的第一件事是输出项目的目录树,并标出每个一级目录的职责。这一步相当于给整个森林拍一张航拍图。命令很简单,直接让 Claude 读取项目根目录结构,然后逐层展开,遇到关键文件(如 package.json、requirements.txt、build.gradle、Dockerfile)就停下来读内容,再反过来告诉我是哪个目录在用它们。
实测下来这种方法有个明显好处:Claude 会先建立"目录-文件-依赖"的映射关系,而不是像搜索引擎一样东看一眼西看一眼。最终它给出的报告里,每个模块都携带了"被谁引用""引用谁"的链路信息,我拿着这份图再去翻具体代码时,基本不会迷路。
1.2 Claude 处理这种任务的边界在哪里
这里必须泼一盆冷水:Claude 再聪明,它看到的也只是你喂给它的那些文件内容。老项目里真正的"隐知识"——比如某段逻辑为什么这么写、某个定时任务为什么只在周二跑——往往不在代码里,而在人的脑子里。
所以我把 Claude 定位成"高并发的信息整理器",而不是"全知全能的架构师"。它能快速帮你完成三类工作:第一,从海量文件中筛选出高信号文件;第二,把散落在各处的框架用法、依赖版本、脚本命令汇总成结构化清单;第三,基于代码特征推测模块职责,供你复核。但涉及业务规则推导、历史原因复盘这类事,它只能提供假设,最终判断权必须留在你自己手里。
提示:送给 Claude 的上下文窗口是有限的,老项目动辄几十万行代码,不可能一次全塞进去。我采用的方式是"分层投喂"——先给目录树,再给关键配置文件,最后给核心源码文件,每一步都让 Claude 把发现写进一份备注,逐步收敛。
2. 我用三个维度把老项目拆成了三层扫描
把 Claude 当侦察兵之后,我给它设计了一套标准的三层扫描流程。这三个维度分别对应技术栈的三个不同切片:装了什么东西、怎么组装、内部结构长什么样。缺任何一个维度,分析报告都是残缺的。
2.1 依赖清单层:从包管理器文件读出"技术栈体温"
第一层最机械也最必要:把项目里所有依赖清单文件翻出来。前端看 package.json,后端看 requirements.txt 或 go.mod,Java 系看 pom.xml,另外还要留意 Gemfile、composer.json 这类历史产物。
我让 Claude 先用代码读取工具把这几个文件全部拿出来,然后生成一张依赖版本清单表。但光列出版本号还不够,我追加了一个指令:"对于每一个依赖,用一句话说明它是干什么的,如果你发现它已经不在主流维护周期内,单独标出来。"这一步立刻让报告从"流水账"变成了"体检单"。比如我发现这个老项目里有一个内部日志组件锁死在某个旧版本上,引用它的只有两处代码,但整个系统启动时都要加载它——这种技术债如果不做依赖分析阶段,后面根本不会注意到。
2.2 构建与配置层:从脚本和配置文件还原"组装方式"
依赖清单告诉你"项目用了哪些零件",但零件怎么拼成整车,要看构建与配置。老项目的构建脚本往往经历了多任开发之手,里面残留着各种时代的痕迹:可能既有 Makefile,又有 docker-compose.yml,还有一个不知道什么时候加的 shell 脚本。
我让 Claude 针对每个构建文件回答三个问题:这个文件负责什么阶段的什么任务?它是否被其他文件引用?直接执行它需要哪些前置条件?特别要检查环境变量。老项目里有些配置直接写在代码里的常量中,有些写在.env.local,还有些藏在启动脚本的 export 里——这些散落的配置项是后来部署时的最大雷区。Claude 把这些配置点位全部捞出来之后,我对着实际部署环境一一核对,还真发现了一个已经失效的数据库连接配置,指向的实例早已销毁。
2.3 架构模式层:从代码调用关系推断"骨架结构"
前两层回答的是"有什么",第三层回答的是"怎么长的"。Claude 可以通过人类可读代码分析工具去查看 C++、Java、Python、JS/TS 等语言的代码库结构,我可以让它针对某个模块阅读源码摘要,然后用一个提示词要求它总结模块间调用关系。
我选择先扫描最核心的主流程入口:比如后端的主应用类、前端的路由表、消息队列的消费者注册处。让 Claude 从这些入口出发,顺着调用链画出模块依赖草图,然后输出一份"关键模块清单及职责推断"。这个过程看起来简单,但实际效果很好。它直接帮我定位到一个被大量模块引用、却没有任何文档说明的中间件模块,后来翻源码才确认那是前前前任开发写的全局拦截器。这种藏在调用关系里的核心组件,靠人工逐文件翻可能得花一整天。
3. 五组提示词的实测对比:同一份代码,不同问法结果差很多
分析过程中我明显感觉到,提示词的质量直接决定 Claude 输出的价值密度。同样一份代码,泛泛地问和定向地挖,结果完全像两个东西在回答问题。我把实测的五组提示词放在一起做了个对比,供参考。
3.1 泛泛而问与定向追问的差距
第一组测试是最朴素的问法:"请分析这个项目的技术栈。"Claude 的回复确实列了框架名和版本,但除此之外全是套话,比如"该项目具有良好的模块化设计"。这种回答听着舒服,实际上等于没答。
第二组我换成了带约束的问法:"请忽略一切修饰性描述,直接输出:核心语言与版本、框架清单、包管理器、构建工具、测试工具、部署方式。每项都必须指出我是从哪里看出来的,否则标注为‘待确认’。"效果完全不一样。Claude 开始引用具体文件路径和关键代码行作为证据,整个报告从"读后感"变成了"尽职调查报告"。
3.2 让 Claude 自己生成追问清单
分析到中间阶段,我遇到了一个典型困境:不知道该往哪里深挖。这时候我有一个非常管用的技巧,把问题抛还给 Claude:
"我已经了解了该项目的依赖和目录结构,但不确定从哪些角度切入才能全面评估它的技术栈现状。请你列出 15 个我下一步应该问的问题,每个问题都要能指向一种潜在的技术债或架构风险。"
这招比我想象中好用得多。Claude 生成的 15 个问题里有几个非常尖锐,比如"数据库连接池配置是否存在硬编码","是否有两个模块重复实现了同一个 HTTP 客户端封装"。这些追问补位了我自己惯性思维容易漏掉的盲区。老项目不怕代码复杂,怕的是你带着"想当然"去分析,追问清单本质上是让模型帮你做了一次对抗性思考。
3.3 给 Claude 一个"输出模板"的奇效
第三组实验是关于输出格式的。同样要分析某个核心模块,第一种问法是"帮我看看这个模块是做什么的",得到的是大段描述,需要自己二次提炼。第二种问法是我预先定义了一个模板:
请按以下格式分析指定模块: 模块名称: 核心职责(不超过50字): 主要输入/输出: 依赖的其他模块: 被哪些模块依赖: 使用的关键技术点: 是否需要重点关注(是/否,原因):效果立竿见影。表格化的输出让多个模块的对比变得极其轻松,我能直接横向扫视哪些模块职责重叠、哪些模块几乎没被引用。更重要的是,这种模板化输出让 Claude 的回答风格稳定可控,连续分析二十个模块后,我能直接把这二十张"卡片"拼成一张全景图。分析老项目时,结构感比文采重要得多。
四组提示词里只有一组让我觉得效果一般:让 Claude 直接读一份几千行的单文件源码并归纳逻辑。这种任务不是它做不好,而是单文件阅读方式绕过了代码之间的调用关系,归纳出来的结论常常浮于表面。后来我改用"先梳理这个文件被谁调用、调用了谁,再读内部逻辑",准确率立刻上来了。
我将五组关键提示词的实测情况整理成了下面的对比表,方便按需取用:
| 提示词类型 | 问题示例 | 实测效果 | 适用阶段 |
|---|---|---|---|
| 泛泛介绍式 | 请分析这个项目的技术栈 | 输出偏套话,缺少证据链 | 基本不推荐 |
| 定向盘点式 | 按指定字段输出技术栈清单并给出判断依据 | 信息密度高,可核查性强 | 项目技术栈初扫 |
| 追问生成式 | 请生成15个下一步要问的问题 | 能补充分析盲区,效果惊喜 | 中期深挖方向 |
| 模板约束式 | 按固定卡片格式输出模块分析 | 输出稳定,易于拼合对比 | 逐模块分析阶段 |
| 单文件归纳式 | 读透某源码文件并总结逻辑 | 结论浮于表面,需结合调用链 | 仅在定位问题时用 |
4. 技术债探测器:Claude 帮我筛出的三处 EOL 隐患
依赖清单出来后,真正的重头戏开始:技术债排查。老项目最危险的地方不在于代码写得差,而在于它和你当下的认知存在巨大时间差。你以为正常的东西,可能早在几年前就被官方判了死刑。
4.1 已停维护的运行时版本
Claude 在分析前端构建脚本时,注意到运行环境的基础镜像被固定在一个非常旧的长期支持版本上。我让 Claude 去查这个版本的生命周期状态,它很快指出:这个版本早就不再有安全更新,而且当前项目的某些语法特性已经超过了它的解析范围。
如果你只是写业务功能,可能一辈子碰不到这个问题,但只要你想在 CI 环境里升级任何构建工具,这个旧运行时立刻会成为拦路虎。我把这个问题标记成 P0:先把运行时版本升级到仍在维护的版本区间,其后再谈其他依赖升级。
4.2 被官方标记废弃的 API 调用
第二种技术债更隐蔽。项目里有一个历史悠久的配置模块,用的是某框架的旧式配置文件写法。这种写法在功能上依然能跑,但官方文档已经明确标注为废弃状态,新版本里默认关闭了兼容开关。
Claude 定位到这个问题的方式很有意思,它在读取项目里的配置文件和启动日志转储文件时,发现有一段警告信息被项目开发者有意屏蔽了——日志级别被调低,导致废弃警告根本没显示出来。这显然不是错误的代码,而是一种"眼不见心不烦"的鸵鸟策略。这种问题靠 AI 自动定位比较难,因为它能运行,只是你不知道它在带病运行。
4.3 隐藏的重复实现与"幽灵依赖"
第三类技术债也值得一提:幽灵依赖。所谓幽灵依赖,指代码里 import 的包不在 package.json 里声明,之所以能跑起来,是因为它还寄生在某个直接依赖的依赖树中。一旦上层依赖升级,幽灵依赖就可能消失,代码当场暴毙。
Claude 在对比依赖清单和源码 import 语句时,发现了多处这类幽灵引用。它无法确定这些引用是故意的还是手滑,但把每个跨版本冲突点都标了出来。我拿到清单后逐一尝试清理,其中两个直接锁定为历史遗留问题处理,另外一个意外的反而是运行时通过全局方式挂载的模块,属于有意为之,但没人写注释。如果没有 Claude 先做交叉比对,这种问题靠人工逐文件 grep 也能找,但效率绝对没有这么高。
5. 从一份分析报告到一份可执行的重构清单
分析到这一步,轮到一个老生常谈的问题:报告写出来了,然后呢?很多技术分析文章死于"写得很好,毫无用处"。我给自己定的规矩是,技术栈分析不是终点,它必须能落到下一步行动里。
5.1 报告的结构化写法
最终我让 Claude 把所有分析结论汇总成一份 Markdown 报告,结构按风险等级排序,而不是按模块顺序排序。主要内容包括四个区块:
- 现状概览:技术栈全景图、模块架构图、关键目录说明
- 遗留问题清单:每条标注风险等级、影响范围、可能的修复路径,以及"问题来源证据"的文件路径
- 升级建议:按依赖优先级排序,附上兼容性说明
- 待人工确认项:Claude 无法从代码中判断的事项,比如"该配置是否仍在生产环境使用""定时任务的执行时机是否有业务硬约束"
我把这份报告作为 Day2 的产出物,直接塞进了项目的 docs 目录。它的价值不只是给我一个人看,后续任何人接手这个项目,先读这份报告就能省下至少两天的摸索时间。这也是"只管去写"最有价值的副作用——你写下的东西,最终会变成团队的共同资产。
5.2 哪些结论可以信,哪些必须人工复核
必须强调:Claude 的分析不是真理。它可能把某个模块的职责推断错,也可能因为没看到业务上下文而给出离谱的"优化建议"。我的复核策略是,所有标为高风险、需要动代码的结论,都必须找到原始代码行做二次确认;所有标为"待确认"的框,必须找一个熟悉业务的人逐一回答。
具体来说,框架和依赖清单这类机械性结论可信度极高;模块职责推断这类语义性结论可信度中等,必须结合代码阅读验证;至于"建议把 XX 重构为 YY"这类建议性结论,我一律只把它当成思路参考,不直接采纳。老项目的病根常常不在技术,而在业务理解,AI 看不到业务,这一点必须保持清醒。
5.3 "只管去写"的真正含义:先写下来,再谈重构
Day2 结束的时候,我并没有立刻开始重构——那不是这个阶段该做的事。我的输出物是一份技术栈分析报告、一张依赖风险清单、一份待确认问题表,以及十几条分析和思考的批注。这些东西全部变成了文字,而不是停留在我的脑子里。
"只管去写"这个动作放到技术分析里,我想表达的其实是:面对老项目,先别急着动手改,先把理解写下来。写的过程会强迫你结构化思考,逼你面对那些"我觉得大概是"的模糊地带。等到有一天真的要重构,这份记录就是你最可靠的方向盘。
如果你也准备用 AI 辅助分析老项目,我的建议是:从目录树开始,按依赖、构建、架构、技术债四个层次推进,过程中的每一步让模型生成结构化输出,最后一定要有人工复核环节。老项目不会因为你用了一个聪明的工具就变得简单,它只是从"一团迷雾"变成了"一张带标注的地图"。剩下的路,还得靠你自己走。