Theme / v1.6.0

Codegraph

程式碼知識圖譜工具

實戰範例

實戰範例 010:改函式簽章前的一次性跨檔依賴盤點

新手友善範例:要修改共享工具函式 formatCurrency 的簽章時,用單次 codegraph explore 列出所有呼叫端,避免 grep 漏掉 import 別名與動態分派。

這個範例要解決的問題

你的 TypeScript 前端專案有一個共享工具函式 formatCurrency,定義在 src/utils/format.ts

export function formatCurrency(amount: number): string {
  return `$${amount.toFixed(2)}`;
}

產品需求來了:「要支援多幣別,formatCurrency 必須加上 currency 參數。」 你預計把簽章改成 formatCurrency(amount, currency)。問題是,這個函式在整個專案被呼叫了 30 幾次,散在元件、Hook、測試、後端 API 回應格式化裡。漏改一個,上線就會炸。

你要在動手前,一次性盤點所有呼叫端


傳統 grep 為什麼不夠

直覺做法是:

grep -rn "formatCurrency" src/

問題馬上浮現:

  1. 註解與字串也會被命中// TODO: formatCurrency should support currency 會被算進去,稀釋信號。
  2. 動態呼叫抓不到const fmt = formatters[currency]; fmt(amount); 這種間接呼叫,grep 看不出 fmt 其實指向 formatCurrency
  3. 改名後的 import 別名import { formatCurrency as fmtMoney } from '@/utils/format'; 在 grep 眼裡是 fmtMoney,不是 formatCurrency
  4. 每次命中還要再 read_file:30 個命中等於 30 次 read_file,光是把上下文湊齊就燒掉數十次工具呼叫。

用 codegraph explore 一次看清楚

先確定專案已經跑過 codegraph init.codegraph/ 存在)。然後:

codegraph explore "formatCurrency src/utils/format.ts"

Codegraph 回傳的結構化區塊會長這樣:

== Definition (src/utils/format.ts) ==
  3  export function formatCurrency(amount: number): string {
  4    return `$${amount.toFixed(2)}`;
  5  }

== Inbound Callers (32 個呼叫端) ==
  直接呼叫:
    src/components/Price.tsx:14       formatCurrency(product.price)
    src/components/Cart.tsx:42        formatCurrency(item.subtotal)
    src/hooks/useCart.ts:21           return formatCurrency(total)
    src/api/orders.ts:88              formatCurrency(order.total)
    src/utils/format.test.ts:6,10,14  測試案例
    ...
  透過別名呼叫 (import alias):
    src/components/Checkout.tsx:7     import { formatCurrency as fmtMoney }
    src/components/Checkout.tsx:23    fmtMoney(cart.grandTotal)
  透過動態分派呼叫:
    src/utils/formatters.ts:30        const fn = registry[name];  // name = 'formatCurrency'

== Outbound Dependencies ==
  - Number.prototype.toFixed         (built-in)

一份輸出涵蓋了 grep 抓不到的三種邊界情境,還附帶每個呼叫端的檔案與行號。


把輸出轉成重構檢核表

收到上面的 Inbound Callers 清單,把它變成一張工作表:

# 檔案 呼叫形式 改動策略
1 src/components/Price.tsx 14 直接呼叫 currency prop 並往下游傳
2 src/components/Cart.tsx 42 直接呼叫 改用 useCart() 已取得的幣別
3 src/hooks/useCart.ts 21 直接呼叫 Hook 簽章擴充 currency 參數
4 src/api/orders.ts 88 直接呼叫 從訂單物件取 currency
5 src/components/Checkout.tsx 23 別名 fmtMoney 同樣改名傳幣別
6 src/utils/formatters.ts 30 動態分派 registry 也要登錄新簽章
30+ 測試檔 6, 10, 14 測試案例 currency 與多幣別 case

重點不是這張表的欄位,而是你確定看過了每一個呼叫端。grep 流程要做到這件事得反覆交叉比對,Codegraph 一次給完。


別漏掉「動態分派」這一類

特別注意上面清單第 6 項。src/utils/formatters.ts 透過 registry[name] 把字串對應到函式再呼叫。這是 grep 的死亡地帶:呼叫端檔案裡根本沒有 formatCurrency 這個字串,只有 registry['formatCurrency'] 的字串鍵。

Codegraph 因為解析了 registry 的型別與賦值鏈,能在 Inbound Callers 的「動態分派」區塊中明確標出這條呼叫路徑。少了這條線索,你的多幣別重構會等到 production 才被 QA 發現「特定幣別還是顯示美元」。


重構完成後的驗證迴圈

簽章改完、所有呼叫端都補上 currency 參數後,再跑一次 explore 確認沒有殘留:

codegraph explore "formatCurrency src/utils/format.ts"

新版清單應該符合兩個條件:

  1. 每個 Inbound Caller 都傳入了 currency 參數(檢查呼叫端形式)
  2. 沒有「透過動態分派呼叫」的黑數殘留(該區塊為空或都已更新)

如果還有漏網之魚,Codegraph 會原原本本列出來。這就是以 Codegraph 做閉環驗證的價值。


整理:影響範圍分析的標準動作

把這個範例歸納成可重複套用的三步驟:

1. 探索前

確認 .codegraph/ 存在。新接手的專案先跑一次 codegraph init,後續 watcher 會自動同步。不確定圖譜新舊時,也可以先跑 codegraph status(v1.6.0 新增)查看索引狀態。

2. 探索中

對於要改動的符號,固定用 codegraph explore "symbol path/file.ts",注意 Inbound Callers 是否出現「透過別名呼叫」或「透過動態分派呼叫」這兩個分類。

3. 探索後

把 Inbound Callers 清單轉成工作表逐項處理。改完再跑一次 explore 做閉環驗證。


關鍵學習點

  • 改共享函式簽章前,用單次 codegraph explore 一次列出所有呼叫端,包括 import 別名與動態分派這兩種 grep 抓不到的邊界。
  • Inbound Callers 的分類(直接、別名、動態分派)可以直接轉成重構工作表,不會漏項。
  • 重構完再跑一次 explore 即可做閉環驗證,確認所有呼叫端都已跟上新簽章。
  • 對 AI 代理而言,這套流程把「影響範圍分析」從數十次工具呼叫壓成一次,正是 Codegraph 標榜的 58% 工具呼叫縮減的典型場景。