iT邦幫忙

2026 iThome 鐵人賽

DAY 2
0
Claude AI

盡信 Claude,不如無 Code — 心法與全端實戰系列 第 2

Day 2 從沒看過的 repo,該從哪裡開始讀?

  • 分享至 

  • xImage
  •  

昨天說,前 20 天講我日常在用的工具。今天第一個遇到的,就是**「陌生專案」**。

而今天只回答一件事:陌生專案該從哪三個檔開始看。


讀 Code 之前,先找閱讀路徑

情境很具體:你 clone 了一個從沒碰過的 repo,444 個 Swift 檔攤在那裡。你要在動手改之前先知道它長什麼樣子。

一般的做法是打開 README、翻 Sources/、找啟動點或是隨便點幾個檔進去看。問題是,這個做法沒有終點。你不知道自己到底理解了多少,也不知道漏掉的,是不是最重要的那個檔案。

知識圖解決的,不是「把知識存起來」,而是規劃閱讀路徑:先算出哪幾個節點連得最多、程式被分成幾群、群跟群之間靠誰接起來,然後先讀那幾個檔

今天要走完這條路:從 clone 一個沒看過的 repo,到最後說得出「先讀這三個」。中間會經過一次撞牆 —— 因為第一個直覺的做法,會把你送進別人的測試套件裡。

今天的實驗對象是 apple/container —— Apple 那套 macOS 容器工具的 CLI 端,commit d6de569

如何開始

⚠️ 注意,它會更動你哪些檔案

graphify claude install 會寫進當前目錄的 CLAUDE.md,並在.claude/settings.json 註冊 PreToolUse hook。在自己的專案跑之前,先確認 git 是乾淨的 —— 跑完 git diff 一眼就看得出來它加了什麼。

在一個乾淨的目錄跑:

git clone https://github.com/apple/container.git
cd container
git checkout d6de5694          # 釘住版本,不然你跟我的數字對不上
uv tool install graphifyy      # PyPI 套件名是 graphifyy,指令是 graphify
graphify claude install        # 把 skill 裝進 Claude Code

然後在 Claude Code 裡下 /graphify .

這裡有個坑值得先講graphify 這支 CLI 本身不負責掃描graphify --help 列出來的是 install / query / path / explain / diagnose —— 掃描是 skill 在 agent 裡跑的多段流程,不是一句 shell 指令。我第一次寫這段時,順手寫成 graphify .。貼回終端機才發現那個指令不存在。

實際跑的時候,我拆成三段來量時間:

階段 做什麼 耗時
偵測 走一遍檔案樹,分類出 code / 文件 / 圖片 0.43 秒
結構抽取 457 個 code 檔跑 AST,14 個 worker 平行 2.41 秒
建圖 + 分群 Leiden 分群、算內聚度、找 god node 1.39 秒
總和 4.23 秒

https://ithelp.ithome.com.tw/upload/images/20260916/20103790pwDGH3c6I2.png

四秒出頭,你就有一張 5,992 個節點、13,881 條邊、330 個群集左右的圖。

「左右」兩個字是認真的,等一下會回來講為什麼。

四秒、零 token、五千九百九十二個節點

這四秒沒有花任何 token。

這件事值得單獨講,因為它跟大部分「用 AI 讀 codebase」的做法不一樣。AST 解析是純程式:tree-sitter 走一遍語法樹,誰呼叫誰、誰繼承誰,都由程式碼的語法結構決定,不需要問任何模型。

「0 token」不是推論的,而是結果:

$ cat graphify-out/.graphify_semantic.json
{"nodes": [], "edges": [], "hyperedges": [], "input_tokens": 0, "output_tokens": 0}

節點零、邊零、token 零 —— 5,992 個節點,一個都不是模型生出來的。

對照一下規模:這張圖之後查一次約 29k tokens,而把等量原始碼直接塞給模型約 403k(這兩個數字是 graphify 自己估的,出自另一輪掃 HEAD 9a8917ca 的收尾輸出;兩版規模差 1%,量級不變)。入場費是零,使用費可以降一個量級 —— 這才是為什麼值得先花四秒建圖,而不是每次都把整個 repo 貼給它。

順帶一句:AST 抽出來的是 15,223 條邊,建圖時併掉了 1,342 條相同端點的重複引用(BuildCommand → String 出現 17 次算一條),所以 13,881 是「不同的關係」的數量,不是「引用次數」。明天會回頭用到這一點。

上面那五行,有三個地方會咬人

第三行的 checkout 不是裝飾。 二十天後我拿新 clone 的 HEAD 重跑:502 個檔變成 512、節點從 5,992 變成 6,052。沒釘 commit 的重現步驟,不叫重現步驟。

**第五行寫出來的 CLAUDE.md,下次重掃會被算成多一個 document 檔。**開頭的警告講的是它會動你的檔;這裡多一層 —— 工具把自己的產物,又餵回自己的輸入,我就是這樣多出第 43 個 md 檔的。

超過 500 檔,它會停下來問範圍,而預設答案會讓你拿到另一種圖。選項一是「Whole repo(Recommended)」,但畫面上同時寫著 42 份文件 + 3 張圖會送進 LLM。我要的是純 AST:讀碼路徑要的是程式的結構,不是 README 講了什麼;而且 AST 是決定性的,跑一百次結果一樣,LLM 不是。**結果會變的東西,不能拿來當對照的基準。**所以選「Type something」,打:

Whole repo, but structural (AST) extraction only — skip semantic extraction.

跑完它會回一句 0 tokens spent (AST-only, as you asked — no LLM call was made)這行就是收據。 沒有它,你不知道自己拿到的是哪一種圖。


同一個團隊的兩個 repo,規模幾乎一樣

之前我掃過它的姊妹庫 apple/containerization(那是底層 library,這次是 CLI)。對照組是同一支指令、同一版工具跑出來的:

https://ithelp.ithome.com.tw/upload/images/20260916/20103790o5iU04UpHP.png

407 個檔、4.49 秒,同樣是零 token。兩份數字擺在一起,第一眼是「怎麼這麼像」:

container containerization
commit d6de569 74ace14
節點 5,992 6,182
13,881 15,855
群集 329–335 315–316
可對回 Package.swift 的 target 79.9% 77.2%
target 數 35 29

規模、密度、分群數,三項都在同一個量級。其實不意外。同一個團隊、同一套 Swift 慣例,圖的形狀自然會有相似之處。

但有一格差很多:

container containerization
群集加權平均純度 83.7–83.9% 87.0–87.3%
邊留在同一個 target 內 61.3% 71.7%
跨 target 的邊 38.7% 28.3%

同樣是 Apple 出品,CLI 那份有 38.7% 的邊是跨模組的,底層 library 只有 28.3%。

這個數字要怎麼讀?它不是「CLI 寫得比較差」。 CLI 本來就是黏合層。它的工作就是把底層的東西接起來給人用,所以跨模組是它的本分。不過把跨模組的邊攤到檔案上,前兩名是兩個測試檔(548、456 條)—— 測試本來就要 import 被測的東西;扣掉測試,真正的跨模組樞紐是 FileManager+AllocatedSize.swift(240 條)和 Parser.swift(186 條)。所以 61.3% 對 71.7% 的差距,有一部分是測試組織方式的差別,不全是產品程式碼的耦合。但這正是圖有用的地方:**你還沒讀任何一行 code,就已經知道這個 repo 的耦合長在哪一層。**接下來要讀的是那 38.7% 的邊穿過哪幾個節點,不是隨便點 Sources/ 裡的檔案。

一個 137 條邊的節點,和它的 99.3%

上個月那次我挖到一項度量:看連接度會誤判,要看 method 邊的佔比。

同樣叫「連接度高」,病因可以完全相反:

  • method 佔比低 = 很多人用我。這是 API 入口該有的樣子,健康。
  • method 佔比高 = 我自己扛太多。那才是 god node。

這次換一個 repo,同一套方法又立刻抓到東西:

repo 節點 總邊 其中 method 佔比
containerization Sendable 500 0 0.0%
container ContainerizationError 447 0 0.0%
container Foundation 314 0 0.0%
container ParserTest 137 136 99.3%

前面三個都是 0% ——很多人用它們,正常。

ParserTest 是唯一一個破 90% 的:**137 條邊裡 136 條是它自己的方法。**一個檔案裡塞了一百多個測試方法,而它在圖上長得跟 god node 一模一樣。但它是測試,不是正式程式碼。

先前在 containerization 上,唯一破百的自帶方法數也出現在測試。兩個 repo,同一種形狀 —— Apple 的正式程式碼守得很好,反而是測試比較鬆。


圖產出來了,然後呢?

到這裡,一張 5,992 個節點、13,881 條邊、330 個群集左右的圖就在硬碟上了。它還附了一份互動網頁,graphify-out/graph.html,打開長這樣:

https://ithelp.ithome.com.tw/upload/images/20260916/20103790Hn0lkCZLbk.png

這是知識圖最常見的死法 —— 圖產出來很漂亮,截圖發個限動,然後就沒有然後了。圖如果不能收斂成「接下來打開哪個檔」,它就只是一張很貴的桌布。

而這張桌布還有一個更具體的問題:放大之後,每一個節點都叫「Community N」。

https://ithelp.ithome.com.tw/upload/images/20260916/201037900IBEiDOU2Q.png

Community 135Community 7Community 306…332 個,沒有一個檔名、沒有一個型別名。點下去,右邊那塊 NODE INFO 也只會告訴你那是第幾號群集。

⚠️ 這不是工具的缺陷,是它的預設保護。 超過 5,000 個節點時它會自動聚合成群集視圖 —— 5,992 個點畫在同一張畫布上,只會糊成一團,那才是真的沒用。想看符號層級要下 graphify export html --obsidian,代價是一個節點一個檔,5,992 個 md。今天用不到。

所以圖不是拿來看的,是拿來的。今天只做一件事:從那張圖走到一份具體的閱讀清單,而且要能驗收。

第一次嘗試:按連接度排,結果撞牆

最直覺的做法是把節點的度數加總到檔案層級,誰的總度數高就先讀誰。跑出來是這樣:

排名 總度數 節點數 檔案
1 751 99 Tests/TerminalProgressTests/ProgressBarTests.swift
2 506 77 Sources/Services/RuntimeLinux/Server/RuntimeService.swift
3 498 20 Tests/ContainerResourceTests/PublishSocketTests.swift
4 459 138 Tests/ContainerAPIClientTests/ParserTest.swift
5 454 69 Tests/ContainerPersistenceTests/ConfigSnapshotDecoderTests.swift
6 398 96 Sources/ContainerBuild/Builder.pb.swift

前五名有四個是測試檔。

⚠️ 先解釋一個看起來矛盾的地方:前面那個 ParserTest137 條邊,這裡卻是 459。兩個都對 —— 只是它們量的不是同一個東西。前面量的是 ParserTest一個節點;這張表是整個檔案,把裡面 138 個節點的度數全部加起來。同一個檔,兩種尺度,而排行榜要用哪一種,本身就是一個決定。

如果我照這張表讀下去,一個下午會花在讀別人的測試 —— 測試當然有價值,但那不是「搞懂這個專案在做什麼」該先看的東西。

而且第六名 Builder.pb.swift 是另一種陷阱:.pb.swiftprotobuf 產生出來的。它度數高是因為它機械地宣告了一大堆型別,讀它,等於讀一份自動產生的樣板。

圖沒有錯,是我問錯問題了。 度數量的是「連得多」,不是「值得讀」。

加兩道過濾器,清單才成立

規則很簡單,兩條:

  1. 丟掉 Tests/ 底下的——測試反映的是驗證方式,不是設計本身
  2. 丟掉產生碼(.pb.swift+GeneratedResources/ 之類)

篩完剩下這三個:

總度數 節點數 檔案 它是什麼
506 77 Sources/Services/RuntimeLinux/Server/RuntimeService.swift Linux runtime 的服務端
398 41 Sources/Services/ContainerAPIService/Client/Parser.swift API 回應的解析
357 44 .../ContainerAPIService/Server/Containers/ContainersService.swift 容器操作的服務端

**這就是今天的答案:先讀這三個檔。**一個 runtime 服務端、一個 API 服務端、一個把兩邊接起來的 parser —— 三個檔看完,這個專案的骨幹,大概就在腦子裡了。


總結

今天從頭到尾沒有讓 AI 介入:graphify 純靠程式走一遍語法樹,四秒、0 token,一個從沒看過的 repo 就變成一張 5,992 個節點的圖。但圖本身不會告訴你該讀哪裡 —— 第一次按連接度排,前六名有五個是測試檔和產生碼;加上兩道過濾器之後,答案才收斂成三個檔:RuntimeService.swift / Parser.swift / ContainersService.swift

這是我現在碰到陌生專案的第一步。它取代的不是 README —— 專案有 README 還是先讀完,那是作者親口說的東西;它取代的是「隨便點幾個檔進去看」那種沒有終點的讀法。

  • 4.23 秒(偵測 0.43 + 抽取 2.41 + 建圖 1.39)、0 tokens,一個陌生 repo 從 444 個 Swift 檔變成 5,992 節點 / 13,881 邊 / 約 330 個群集
  • 建圖時併掉 1,342 條同端點的邊 —— 13,881 是「不同的關係」不是「引用次數」
  • CLI 那份有 38.7% 的邊跨模組,底層 library 是 28.3% —— 讀碼要從那 38.7% 下手
  • ParserTest:137 邊、99.3% 是自己的方法,唯一一個真的 god 形狀,而它是測試
  • 未過濾的排行榜:前六名有五個要砍掉(四個測試檔 + 一個 protobuf 產生碼)
  • 過濾後的答案:RuntimeService.swift / Parser.swift / ContainersService.swift

這一篇留下的心法:

**圖給你候選,不給你答案。**看連接度會誤判,要看 method 邊的佔比 —— 低佔比是「很多人用我」,高佔比才是「我自己扛太多」。而就算換了這個指標,度數量的仍然是「連得多」,不是「值得讀」—— 測試檔和產生碼永遠會排在前面,因為它們機械地連了一堆東西。一張沒有過濾器的排行榜,會很有自信地把你送進別人的測試套件裡。

明天:換 AI 來讀同一個 repo,不給圖、也不讓它讀 Package.swift,讓它寫一份架構文件。我逐句對賬它有沒有編,再看它挑的入口檔,跟今天這三個對不對得上。


參考資料

  • 本文所有數字掃的是 apple/containerd6de5694(2026-08-20),不是撰稿當下的 HEAD;對照組 containerization74ace148(2026-07-27)。兩份都用 graphifyy 0.9.22 重新建圖後才拿來比 —— 版本不同的工具產的兩張圖不能擺在一起
  • apple/container,commit d6de5694200468d99a61662bfb9bb3aba763e3e5github.com/apple/container
  • apple/containerization(對照組),commit 74ace148ded72f7bb3c878b142e4962ae668adf4github.com/apple/containerization
  • graphify(PyPI 套件名 graphifyy,MIT),本文所有數字都出自 0.9.22github.com/Graphify-Labs/graphify 安裝與呼叫:uv tool install graphifyygraphify claude install → 在 agent 裡下 /graphify <path>
  • 本文兩張對照表與排行榜的度量腳本 graph-metrics.py,輸入是 graphify-out/graph.jsongithub.com/n913239/Medium/tree/main/tools/graph-metrics README 寫明了歸屬規則(節點的 source_file 路徑中若出現 Sources/Tests/,取其後一段當 target,其餘視為無法歸屬)與純度的分母(群集內可歸屬的節點數),你可以拿去打自己的 repo
  • 延伸閱讀:圖只負責告訴你該讀哪幾個檔——用知識圖解析一個完全陌生的 Apple 開源專案 —— 先前用同一套流程讀 containerization 的完整記錄;對照組那欄的原始數字出自這一篇

上一篇
Day 1 Claude 寫的 code,人機 審的 code
下一篇
Day 3 讓 AI 讀同一個 repo
系列文
盡信 Claude,不如無 Code — 心法與全端實戰9
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言