接手两个陌生的老系统:AI 4 分钟写完架构文档,我花了 25 天才真正看懂

AI实战代码阅读系统接手架构梳理Java

接手两个陌生的老系统:AI 4 分钟写完架构文档,我花了 25 天才真正看懂

TL;DR 9 月初我换了个组,要接手两个完全陌生的老系统。AI 只用 4 分钟就交出了第一份架构文档,可我读完的反应是:“还是没懂。”

这篇复盘接下来 25 天我是怎么用 AI 把这两个系统真正读懂的:换着角度提问,拿日志和数据库去核对它的答案。 过程中我挑出了 AI 的 4 处错误,还顺带挖出一个藏得很深的坑:容器只给了 8 核,Java 却当成 40 核来开线程。

先交代一句:上一篇结尾预告说这篇讲“代码图谱”。翻完聊天记录我才发现,这 25 天里 AI 几乎没用到图谱工具,靠的就是搜代码、读文件,再拿日志和数据库来核对。所以这篇就照实写了。


一、背景:两个“输入输出很简单”的系统

9 月 2 日,我写完了老项目的交接文档。第二天就开始接手新组的两个核心服务:

  • 风控审批服务(机审服务):用户申请借款时,由它来决定“批不批、批多少、要不要转人工审核”。打个比方,它像一个审批总指挥:查征信、拉风控指标、送进决策引擎打分,最后给出结论。
  • 风控变量平台:审批服务需要的几千个风控指标都从这里取。它像一个中央厨房:业务方只管点菜(我要这几组指标),厨房负责去各个供应商那里采购原料(调外部数据源),再加工成菜(计算出指标值)端出去。

这两个系统都有个共同特点:接口看起来非常简单,传进去几个参数,返回一堆指标。但代码里的实现层层嵌套,配置项、缓存、线程池绕来绕去。

我手里的工具就两样:两个 AI 编程助手(Claude Code 和 Antigravity,哪个顺手用哪个),外加我的本地代码和登录线上机器查日志的权限。


二、第一份文档:4 分钟写完,但我没看懂

9 月 3 日 17:07,我给 AI 的第一句话是:

“帮我看看这个服务,我刚接触,以后要开发,你来做一个业务梳理的文档,看看这个服务是做啥的,有哪些功能,输出一个文档。”

17:11,文档就写好了。 有架构图,有 22 个对外接口的分类表,有流程引擎的节点类型,有存储设计,最后还附上了“开发避坑点”。看起来非常专业。

44 分钟后,17:55,我回了一句:

“还是没懂,这个服务本身是做了哪些业务,外部依赖了哪些,从业务的角度讲讲。”

AI 换了一种写法:按一个借款人从申请到放款的过程,一步步讲这个服务在每一步做了什么:借钱前的初筛、申请额度时的全套机审、发现疑点时的补充核验、提现时的二次审核……这一次我看懂了。

问题出在哪? 第一份文档是按代码结构组织的(有哪些模块、哪些类)。可一个新人最先需要的是业务地图:这个系统在整个业务里扮演什么角色,它的上游是谁,下游又是谁。代码结构要等有了业务地图以后,才知道往哪里放。

用“如果……会怎样”来提问

后面几天,我发现提问效果最好的方式是假设一个故障。比如 9 月 7 日我问:

“看下 mongo、mysql、redis 是啥关系,如果 mongo 挂了,会影响流程吗?”

光问“三个库分别存了什么”,AI 会给一张很全但记不住的表。换成问“挂了会怎样”,它就得去代码里找哪些地方是强依赖。结论很明确:Mongo 一挂,进件、结果落库、挂起流程的唤醒全部会失败。这样我立刻就知道了它在系统里的分量。

可带走的经验:

  1. 让 AI 写接手文档时,先要业务视角,再要技术视角。直接说“我是新人,从业务角度讲”;
  2. 想搞清楚某个组件有多重要,就问**“如果它挂了会怎样”**,比问“它是干什么的”有用得多。

三、上线了一个需求,也不等于看懂了

9 月 14 日,我在变量平台接了第一个需求:接入一批新的离线数据作为风控变量。AI 帮我理清了配置之间的关系,写好了上线 SQL 和页面操作步骤。第二天顺利上线,测试场景也能拿到分数。

但 9 月 16 日我跟 AI 说了实话:

“感觉这个服务的输入输出看着很简单,就是查询特征变量集合,但其实实现还是很复杂的,一时半会还无法完全理解整个架构和实现。”

“昨天上线已经完整操作了一遍,但是还是不理解详细的数据模型和结构,只是大概知道这么配置能跑通和上线。”

于是我换了个读法:不再求一次看懂全貌,而是从一个具体的点往下挖。 我选的起点是“数据源”这张配置表,问 AI:数据源 ID 为什么要用固定的前缀开头?“数据源类型”这个字段只是个标记,还是真的会影响执行逻辑?

AI 的回答里有三个细节,让我对它的用法有了新认识:

1. 源码不在仓库里,它就去读 jar 包。 数据源的实体类在一个单独发布的依赖包里,本地没有源码。AI 直接去 Maven 本地仓库里读了 jar 包里的字节码,把字段的枚举定义找了出来。

2. 它分清了“代码真的在用的规则”和“给人看的命名习惯”。 它搜了所有对数据源 ID 做前缀判断的代码,结论是:只有 5 个前缀被代码判断过,其中 4 个会被改写、路由到特定的取数插件,1 个在特殊场景下被排除。其他前缀全是人为约定,方便看 ID 就知道数据从哪来,代码根本不管。另外它还发现了一个真正的硬约束:这个 ID 字段最长 20 个字符,超出的部分会被截断,导致关联查不到数据。

3. 它纠正了我自己的记忆。 我问:“那个需求的数据对变量平台来说是内部数据,可我 SQL 里填的数据源类型是外部数据,有影响吗?”

AI 去翻了我那份上线文档里的 SQL,回答是:你实际填的值是 2,代码枚举里 2 代表“内部数据”。 是我自己记错了。数据源名称里写着“外数”,所以我一直以为填的是外部数据。

可带走的经验:

  1. 系统太大读不动时,挑一个具体的配置项或字段,一路追到底,比试图一次理解整个架构有效得多;
  2. 让 AI 区分**“代码真正依赖的规则”和“团队的命名习惯”**,前者改了会出事,后者只影响可读性;
  3. 不光要核对 AI,也要让 AI 拿代码核对你自己的记忆。

四、光看代码不够,拿一条真实请求的日志对照着看

读到核心查询流程时,代码里有调度器、任务、回调、几个线程池,静态地看怎么也串不起来。9 月 18 日我提了一个要求:

“你到线上日志拉取真实数据,根据数据来说明,并补充到图里,方便我根据数据和流程图整体理解代码执行的细节。”

后来我又让它更进一步:

“再拉取一次完整请求的日志,将这次请求的完整日志 grep 出来,放到本地文件,我对照着代码和日志再过一遍。”

它拉回来的第一条请求耗时不到 1 秒,很多分支都没走到。我又补了一句:

“这个耗时不到一秒,找一个耗时长的,走完大部分流程的。”

挑一条“走得最远”的请求,这个小技巧很管用。一条完整的日志就是一份按时间排好序的执行记录:哪个线程在什么时候拉了什么数据,花了多少毫秒,结果什么时候回来,哪个计算步骤接着开始。拿着它对照代码读,比单看代码清楚得多。

可带走的经验:静态代码告诉你“可能怎么走”,日志告诉你“实际怎么走的”。读复杂流程时,让 AI 拉一条完整的真实请求日志放到本地,而且要挑耗时长、分支多的那条。


五、AI 写错的 4 个地方,我是怎么挑出来的

这 25 天里,AI 给出的结论大部分是对的,但并不全对。下面这 4 次,都是我拿别的证据核对时发现的:

#AI 当时说的我拿什么核对的实际情况
1画了一张图,说某个变量组展开后得到 3 个征信类变量,还给每个变量配上了含义(“近 3 月查询次数”等)自己去数据库查了这个组的配置这个组下面只有 6 个位置类变量。那 3 个征信变量来自同一次请求里的另外几十个组,图上标的变量含义也没有出处
2文档里说这个接口单次内部耗时 4 秒左右监控大盘上的最大耗时只有 434ms两个都对:监控曲线是按时间段聚合的平均值,被大量几十毫秒的快请求拉低了。AI 去机器上把单次请求的耗时排了序,最慢的确实有 3 秒多
3流程图里,模型打分之后直接存结果我追问“模型打分后不需要走算子加工吗”,让它拉一条实时日志看日志显示模型返回的是一整包 JSON,还要经过“算子”把需要的分数取出来,图上漏画了这一步
4线程池大小按机器核数来算云平台上这台容器标的是 8 核容器里的 Java 认出来的是 40 核。这一条牵出了一个真正的暗坑,下一节单独讲

第 1 条最值得警惕。AI 画的图非常像回事:变量 ID 是真实的,流程也对得上,唯独“这些变量属于哪个组、分别是什么含义”这一层是它推测出来的,而且图里看不出哪些是推测。如果我没去数据库查那一下,这张图就会作为“正确理解”写进文档。

还有一个细节值得一提。9 月 7 日我说“架构图里的上下游关系,我理解是相互依赖,而不是简单的上下游”,AI 的第一句回复是:“你的理解非常透彻,完全正确!” 我的理解大体是对的,可 AI 这种“先肯定”的习惯,意味着它的认同本身不能当证据。

可带走的经验:

  1. AI 给出的具体数字、ID 和对应关系,至少抽一个去数据库或日志里核对;
  2. 两个数字对不上时,不要急着判定谁错,先让 AI 解释为什么对不上,并拿出证据(比如第 2 条,其实两个都对,只是统计口径不同);
  3. AI 说“你说得完全正确”时,这句话本身不提供任何信息。要看它后面给出的代码证据。

六、意外挖到的暗坑:容器只给 8 核,Java 当成 40 核

第 4 条的经过是这样的。

那天下午我在对着代码梳理调度器和线程池的关系,突然想起云平台上这台机器标的是“8 核 / 10G”,就问了一句:

“线上机器不是 8 核吗?”

AI 登录那台机器,执行了 nproc(查看系统能识别到几个 CPU 核),返回的是 40。

原因是这样的:

  1. 容器是“分到的一小块”:这台容器跑在一台 40 核的物理机上,云平台只是通过一种叫 cgroups 的机制,限制它最多用 8 个核的计算量;
  2. 老版本 Java 看不到这个限制:服务用的是 JDK 1.8.0_144。Java 8 要到 8u191 才开始默认识别容器的 CPU 限制,更早的版本会直接读到整台物理机的 40 核;
  3. 线程池按核数开:代码里两个线程池的大小都是按核数算的:
private static int CPU_CORE_NUMS = Runtime.getRuntime().availableProcessors();
// 取数线程池:核数 × 2
new IOIntensiveTaskExecutor(CPU_CORE_NUMS * 2, CPU_CORE_NUMS * 2, ...);
// 计算线程池:核数
new ComputeIntensiveTaskExecutor(CPU_CORE_NUMS, CPU_CORE_NUMS, ...);

按 40 核算下来,结果就是:一个只有 8 核的容器,开了 80 + 40 个工作线程。 取数线程大部分时间在等网络返回,多开一些问题不大;但计算线程是实打实地占 CPU,40 个线程挤 8 个核,大量时间会浪费在线程切换上。

这类坑的特点是:代码看起来完全没问题(按核数配线程池是标准写法),配置看起来也没问题(平台上写着 8 核),只有把“代码 + 运行环境 + 真实机器”放在一起看才能发现。我随后让 AI 把这个问题补进了查询流程的分析文档,作为后续改造的输入。

同一时期还挖到两个小坑,类型相近:

  • 两个取数插件注册了同一个数据源 ID:代码里有 8 个数据源 ID 被两个类同时声明。有的旧类没有在加载清单里登记,属于死代码;但至少有一个 ID,两个类都登记了,最终谁生效取决于加载顺序,后加载的会覆盖先加载的;
  • ID 字段长度限制:前面提到的 20 字符截断。

可带走的经验:

  1. 让 AI 做性能分析时,把真实的机器配置告诉它,并让它登录机器核实,不要让它对着代码空推;
  2. 用 Runtime.availableProcessors() 配线程池的老服务,如果跑在容器里,先确认 JDK 版本是否 ≥ 8u191,否则线程数可能是你预期的好几倍;
  3. 插件化的系统要查一下有没有同一个 key 被重复注册,覆盖顺序往往没人记得。

七、最后一步:画一张“概念关系图”

到 9 月 28 日,具体流程基本都理清了,但我发现自己跟别人讨论时总是说不清楚:场景、变量组、变量、特征、数据源、算子,这些概念之间到底是什么关系? 于是我让 AI:

“把服务涉及到的概念,比如场景 / 变量组 / 特征 / 变量 / 数据源 / 模型算子等,画图列出关系,比如一对一、一对多,体现这种关系。”

简化以后大致是这样的:

场景 ←—— 多对多 ——→ 变量组
                       │ 一对多
                       ▼
                    应用变量(返回给业务的那个)
                       │ 多对一
                       ▼
          算子 ←一对一— 变量(代码里叫“特征”)
                       │ 一对多(通常 1 条)
                       ▼
                    取数配置 ——→ 数据源 ——多对一——→ 取数插件

画这张图的过程,是这 25 天里来回讨论最多的一次:

  • AI 顶回了我的判断。 我问“数据源和取数插件不应该是多对多吗?”,AI 回答“不是”,并拿出了证据:插件的注解只能填一个数据源 ID,启动时放进一个 Map,一个 key 只对应一个类。所以是多对一。前面说的“重复注册”问题,就是在这一步查出来的。
  • AI 修正了自己。 我问“应该叫变量组还是特征组?”,AI 去翻了变量平台的前端页面代码,发现页面上的“变量”其实就是代码里的“特征”,于是主动修正:“上一轮我说‘变量是返回给业务的、特征是内部叫法’,这个说法不准确。”
  • 我挑出了文档里的一处出入。 我对照着配置类读代码,发现文档里关于“特殊场景”的一处描述和代码对不上:场景 ID 来自机器上的配置文件,配置中心里配的只是这个场景的行为开关。AI 改完以后,又主动指出同一张表里还有一行和代码不符(特殊场景下缓存其实只读不写)。

平台页面上的叫法和代码里的叫法对不上,是老系统接手最大的障碍之一。这张图真正的价值,就是把两套叫法对齐了。

可带走的经验:读到最后,让 AI 画一张“概念 + 数量关系”的图。画的过程中,你和 AI 对同一个词的不同理解会全部暴露出来。


八、我的接手流程,可以照抄

把这 25 天的做法整理一下:

阶段做什么给 AI 的典型提问
1. 业务地图先搞清楚系统在业务里的角色“我刚接触这个服务,从业务的角度讲讲它做了什么、依赖了谁”
2. 关键依赖找出系统离不开的组件“如果 XX 挂了,会影响哪些流程?”
3. 单点深挖挑一个配置项或字段追到底“这个字段是真的会影响执行,还是只是个标记?代码在哪?”
4. 日志对照用一条真实请求对照代码“拉一条耗时长、走完大部分流程的完整日志到本地”
5. 交叉核对拿数据库、监控、机器配置核对 AI 的结论“这和监控 / 数据库 / 机器配置对不上,解释一下并给出证据”
6. 概念对齐画概念关系图,统一叫法“把这些概念画成图,标出一对一、一对多的关系”

为什么这次没用到代码图谱?

我本地配好了代码图谱工具(它能快速回答“谁调用了这个方法”“改这里会影响哪些地方”)。可翻完聊天记录,这 25 天里 AI 几乎没用它,主要动作就是普通的搜索和一个个打开文件读,一个会话里打开上百次文件是常事。

回头想,原因是**“看懂一个系统”和“改代码前评估影响面”是两件事**。代码图谱擅长后者:调用关系、影响范围。可接手时我真正卡住的问题,比如“这个字段到底起不起作用”“机器上实际开了多少线程”“这个组下面到底有哪些变量”,答案不在调用关系里,而在数据库、日志和机器上。

可带走的经验:接手老系统时,最重要的“工具”不是某个分析器,而是让 AI 能同时碰到代码、日志、数据库和真实机器,并且你愿意花时间去核对它的结论。


结语

这 25 天里,AI 帮我省掉的是大量的“体力活”:翻几百个文件、读 jar 包字节码、登录机器拉日志、反复重画图。换我自己硬啃,时间肯定要长得多。

但它没法替我“看懂”。真正让我看懂的,是那几次**“还是没懂”“和监控对不上”“机器不是 8 核吗”**。每一次怀疑,都逼着 AI 去找更硬的证据,也逼着我自己去对照代码。

AI 能把文档写得很快,可文档写好了不等于人看懂了。

下一篇:一个跨两个服务的小需求:AI 写代码只用了 6 分钟,剩下一天半花在了哪


👉 开源: Solo Workspace (独立开发者 CLI)· solo-spring-scaffold (Spring Boot 脚手架)

👉 其它平台: 知乎 · 掘金

👉 联系: [email protected] (合作 / 线上问题交流欢迎来信)