我的好朋友 Claude
第 118 期|Claude Code|創作者、打工仔|

Claude Code 揾效能瓶頸:先量度、後收窄、再修正三段式 workflow

頁面載入慢、API 慢、build time 暴增。一開口就叫 Claude「優化」,佢會出一堆零碎小改動。教你三段式 workflow:先 profile 攞到可量度嘅數據、收窄揾出最痛嗰三個位、一次只修一個再重新量度。冇量度過就唔好優化。

難度 ★★★時間 60 分鐘用具 Claude Code CLI、你個 app + profiler (Chrome DevTools / py-spy / etc.)
【編者撰】一個香港人

情境

你嘅 HK e-commerce site,舊年 LCP 1.8 秒,而家 4.2 秒。或者你個 SaaS API,上個月 p95 180ms,而家 950ms。又或者 pnpm build 由 45 秒變 3 分鐘。

你做嘅第一件事,多數係咁:

  1. 開 Claude Code,貼個 component / endpoint 嘅 code 落去
  2. 打:「優化呢段 code,太慢」
  3. Claude 出咗 5 個建議:useMemo 包多幾個 value、加 lazy load、改 SELECT 用 index、拆細 bundle…
  4. 你全部用晒,重新部署。LCP 由 4.2 秒變 4.1 秒。
  5. 你問:「仲有冇得優化?」Claude 再出 5 個。又用晒。3.95 秒。
  6. 過咗 3 個鐘,砌咗 50 個零碎小改動,p95 先改善 12%。你開始懷疑人生。

呢個係典型 AI 輔助優化嘅反面教材

紀律:冇量度過就唔好優化。下面三段式 workflow 係先量度、後收窄、再修正,每段都有條死規矩。

跟住做

1. 揀啱量度工具(10 分鐘)

落手前,揀啱 profiler。冇 profiler 就唔好開始。

場景Tool量度乜
前端 page loadChrome DevTools Performance + LighthouseLCP / INP / CLS / TBT
前端 runtime(React render)React DevTools ProfilerComponent render time
Backend API (Node)clinic.js / 0x / built-in --inspectFlame graph、event loop lag
Backend API (Python)py-spy recordCPU sampling
Backend API (Go)pprofCPU + heap
Database queryPostgres EXPLAIN ANALYZE / slow query logQuery plan + time
Build timevite --profile / webpack-bundle-analyzer / esbuild --metafilePlugin / chunk cost

⚠️ 唔好估邊樣慢。你以為「實係 React render」,profile 出嚟可能係個 500KB 嘅 lottie animation 阻塞咗主執行緒。

2. 擷取 profile,再餵畀 Claude 分析(15 分鐘)

跑 profiler,將原始輸出儲落 file(唔好淨係睇 GUI screenshot)。

# 例:Node API 用 clinic flame
npx clinic flame -- node server.js
# 跑 load test 5 分鐘
# Stop → 出 flame.html + .clinic-flame folder

# 例:Python 用 py-spy
py-spy record -o profile.svg -- python app.py
# 跑 typical workload

# 例:Postgres slow query
# 已開 log_min_duration_statement = 100ms
tail -n 500 /var/log/postgresql/postgresql.log > slow.log

然後喺 Claude Code 入面,唔好貼 code,先貼個 profile

Profile 分析 prompt(收窄階段入口)
我喺度 debug 一個效能 regression。徵狀:[頁面 LCP 4.2 秒 / API p95 950ms / build 3 分鐘]。我啱啱跑咗 [clinic flame / py-spy / Lighthouse / EXPLAIN ANALYZE],原始輸出喺 (已附上)。

呢個 prompt 嘅紀律:禁止 Claude 跳去修 code。先量度、後收窄、再修正三段,每段唔好溝埋一齊。

3. 收窄到頭號元兇,形成假設(15 分鐘)

Claude 揾到頭號元兇(譬如:getOrdersWithItems() 食咗 67% wall time),下一步唔係修,係確認 root cause

開個跟進 prompt,餵嗰個特定 file:

確認 root cause prompt
Profile 顯示 getOrdersWithItems()(src/api/orders.ts,已附上)食咗 67% wall time。我用 Postgres slow log 睇到呢個 endpoint 每個 request 出 51 個 query。請:
逐行讀晒 getOrdersWithItems
講邊一行造成 51 個 query(精確到行號)
分類:呢個係 N+1、定重複抓取、定缺 index?
你呢個假設嘅信心:高 / 中 / 低?如果中或低,建議再擷取乜嘢數據去確認。
仍然唔好提議點改 code,淨係確認原因。

呢一步嘅意義:你逼 Claude 做診斷,而唔係開藥。診斷錯,開咩藥都一定錯

4. 一次修一個,修完即刻重新量度(20 分鐘)

確認咗 root cause 先入修正階段。一次只修一個元兇,修完即刻重新量度。

# Cadence 流程
1. Baseline measurement → 寫低(e.g. p95 = 950ms)
2. Fix top-1 culprit
3. Re-measure → 寫低(e.g. p95 = 220ms)
4. Δ = 730ms = 77% improvement → 真係嗰個 culprit
5. 仲未達標?返 step 1,攞新 profile,揾新 top-1
單一修正 prompt(已確認 root cause)
已確認:getOrdersWithItems 第 42-58 行係 N+1(喺每個 order 嘅迴圈入面再 query items)。請提議一個修正:
改 query 用 JOIN 一次過攞,或者用 WHERE order_id IN (...) 批量抓取
出 diff,淨係改呢個 function
列出可能會搞壞嘅情況(譬如空嘅 order list、order 數量過 1000、items table 有 soft delete)
列出我應該加邊個 test case 去防 regression
唔好順手執埋其他嘢。我要一個孤立嘅 diff,方便重新量度。

紀律:一個 PR 一個修正。你溝埋兩個修正,重新量度嗰個 Δ 就唔知邊個貢獻幾多。下次 regression 你又冇得逐個 bisect 揾返。

變化

變化 1:前端頁面載入(Web Vitals + Lighthouse)

HK e-commerce 最常見:手機 4G LCP 5 秒。流程:

  1. 量度:Chrome DevTools → Lighthouse(Mobile,Slow 4G throttle),儲存個 JSON report
  2. 收窄:餵個 JSON 落 Claude,問「最拖累 LCP 嘅頭三樣嘢」。通常會講出阻塞渲染嘅 JS / 大 image / 第三方 script
  3. 確認假設:對最大嗰個譬如「Hero image 1.8MB」,問 Claude「<Image> component 點設定先用到 next-gen format 同 responsive sizes」
  4. 修正再重新量度:一次只改一樣(譬如轉 AVIF),重新跑 Lighthouse,寫低 Δ

⚠️ 唔好喺同一個 PR 入面又轉 image format、又 lazy load script、又 inline critical CSS——你重新量度嗰刻分唔到邊個有效。

變化 2:後端 API(database / N+1)

SaaS dashboard 開頁 7 秒,p95 950ms。先跑 EXPLAIN ANALYZE

EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM orders WHERE workspace_id = $1;
-- 留意 Seq Scan vs Index Scan、Rows Removed by Filter、loops

餵個 plan 落 Claude,問「呢個 plan 邊度唔合理」。Claude 通常即刻撠到:缺 index、SELECT * 拉太多 column、subquery 應該改寫成 CTE。

關鍵:唔好淨係問「優化呢個 query」。要問「呢個 EXPLAIN plan 邊行成本最高,點解」。前者係叫 Claude 估,後者係叫 Claude 讀數據。

N+1 嘅特徵:log 入面一個 100ms 嘅 request 對住 50 個形狀一模一樣嘅 query。Prisma / ActiveRecord / SQLAlchemy 都各自有 eager-load 嘅寫法(include / includes / selectinload),叫 Claude 直接出返嗰個 framework 慣用嘅寫法。

變化 3:Build time(webpack / vite bundle analyzer)

pnpm build 由 45 秒變 3 分鐘,CI cost 飛升。

# Vite
vite build --profile  # 出 profile JSON
npx vite-bundle-visualizer  # 視覺化 chunk

# Webpack
webpack --profile --json > stats.json
npx webpack-bundle-analyzer stats.json

餵 JSON 落 Claude,問「邊個 plugin / 邊個 dependency 食咗最多 build time」。通常元兇係:

修完即刻再跑一次 time pnpm build,記低 Δ。CI 加咗 cache key 之後再記多次冷啟動同熱啟動嘅分別。

拆解:點解 work,同邊度會仆街

跟到上面就已經夠用。下面呢段係畀**想由「跑一次 profile 揾到嘢」做到「次次量度都信得過」**嘅人——初學者可以跳過,唔影響你跟住做。

效能調優最容易呃自己嘅地方係:個數字郁咗,唔代表你真係改善咗件事。你以為收窄到元兇,其實量度本身就已經呃緊你。呢個 workflow 實際會喺呢幾個位爆,你要預咗:

1. Profiler 自己拖慢咗你個程式(observer effect) 好多 profiler 用 instrumentation(喺每個 function 入口插 hook)去計時,呢樣本身有開銷。開咗 profiler 嗰刻,啲 function call 多嘅細 function 會被放大到睇落好似好食時間,但實際生產環境根本唔係咁。

2. 你量度嗰個 workload 唔代表真實流量 你喺 dev 機跑五分鐘 load test,但個 N+1 喺資料量細嗰陣可能完全唔覺。生產有十萬條 order,dev 得五十條,個 query plan 連 Postgres 揀唔揀 index 都可能唔同。

3. Re-measure 個 Δ 其實係雜訊 你修完一個位,p95 由 950ms 跌到 920ms,你開心咗。但你只跑咗一次,背景有個 cron job、有 GC、有鄰居 VM 搶 CPU——呢 30ms 可能純粹係 run-to-run variance。

4. Claude 睇嘅 profile 係截斷版,唔係全貌tail -n 500 條 slow log,或者貼咗半個 flame graph 落去——Claude 只睇到你畀佢嗰嚿。佢唔會知道你剪走咗嘅嗰半係咪藏住真正元兇,但佢答得好肯定。

5. 修一個瓶頸,會令下一個瓶頸浮出嚟(Amdahl 陷阱) 你修好咗食 67% 嗰個 N+1,p95 跌一大截,正常。但跌完之後,原本佔 10% 嘅另一個位,而家可能變咗佔 40%——瓶頸搬咗位,唔係冇咗。

呢幾個位,就係「跑一次 profile 揾到嘢」同「次次量度都信得過、改善真係落到生產」之間嘅距離。

一個紀律

冇量度過就唔好優化。冇收窄到就唔好修。

Claude 唔係慢——係你冇畀 profile 佢睇,佢唯有撩 codebase 入面啲「教科書 hotspot」。呢類 hotspot 通常只改善 3-5%,但就耗你 3 個鐘。真正嗰個食咗 80% 嘅瓶頸,profile 一跑就睇到,收窄 5 分鐘就確認到。

你嘅工作:

Claude 嘅工作:

呢個分工做得好,效能 regression 由「3 個鐘 50 個小修補」變「30 分鐘 1 個 PR」。做唔好,你就會喺 useMemo 同 SELECT column 之間兜圈。

下次撞到「個 app 變慢」,收手 10 分鐘:揀 profiler、儲存個原始輸出、再開 Claude。先量度、後收窄、再修正三段,紀律守得住,瓶頸自己就會浮返出嚟。

文中工具 · 連結

  • 開發者用 — terminal 入面同 Claude pair coding

睇完想同 Claude 一齊行一次?

撳一撳,就將成段 tutor 指示(連埋成篇文嘅內容)抄入剪貼簿。 貼入 Claude.ai 或 Claude Desktop,佢會用廣東話帶你一步一步行, 每步問你填關鍵位,最後畀返一個專為你情況寫嘅 prompt 帶走。

下期預告 · 相關情境
訂閱本副刊

每週日早上,
一道新菜送到你 inbox。

一篇 use case、一個香港情境、一個跟得到嘅做法。 冇 sell course、冇話你「再唔學就會失業」。

訂閱通道執緊緊
newsletter service 仲未接通。想第一時間收到新文章——
直接 email 我哋寫一句「訂閱」就得。

Email 「訂閱」畀我