Tips & Tricks (更新: 2026/7/23)

処理が遅いNode.jsを推測で直すな:計測でボトルネックを特定して速くする手順

遅いNode.js処理を勘で直さず、計測でN+1・直列await・再計算を見つけ、Claude Codeに安全に直させる手順。

処理が遅いNode.jsを推測で直すな:計測でボトルネックを特定して速くする手順

「このAPIとCSV出力、昨日から遅い。release前に直して」

そう言われて、すぐDBインデックスを足したくなる。僕も前はそうでした。けれど、計測しないままコードを直すと、作業時間を使ったのに遅延が1分も縮まらない失敗が起きます。遅い場所を見ずに、見た目が怪しい場所だけ直すからです。

この記事は、Node.jsのサーバー処理、API handler、batch script、CSV exportを対象にします。ブラウザ表示、画像、layout shiftの話は別なので、そちらは Core Web Vitalsを測ってから直すWebパフォーマンス改善 を見てください。SQLの中で時間が消えているなら、次に SQLが遅い原因をEXPLAINで突き止める手順 を読む流れです。

ここでのボトルネックとは、処理全体の速度を決めている一番遅い区間です。プロファイリングとは、実行時間を測ってその区間を見えるようにする作業です。Claude Codeには推測で書き換えさせず、まず数字を出させます。

この記事の要点

  • まず再現コマンド、fixture、API入力、期待結果を1つに絞る。
  • performance.now() で区間ごとの時間を出し、遅い順に並べる。
  • N+1、直列 await、無駄な再計算、二重探索を順に疑う。
  • Claude Codeには計測コードとテスト更新を任せ、本番データ、secret、billing、deployは人が見る。
  • 直した後も同じコマンドで確認し、数字が出ないspeed claimは書かない。

数字なしで「速くなった気がする」と言うのは危険です。問い合わせ率、予約完了率、作業時間、SLAの話につながる処理ほど、直す前と後のコマンドを残してください。

まず計測対象を1つに絞る

API、CSV、テスト、URL、コマンドのどれを遅い対象にするか、最初に決めます。「プロジェクト全体を速くして」は広すぎます。Claude Codeが関係ないファイルまで読み、会話が重くなり、最後に何を確認したのか分からなくなります。

よい依頼は、対象ファイル、再現コマンド、使ってよいfixture、人が承認する範囲を先に書きます。次のように、修正より前に計測を要求します。

claude -p "src/api/report.ts が遅い。まだ直さないでください。
DB取得、外部API取得、変換、レスポンス整形に performance.now() の計測を入れてください。
使うデータは tests/fixtures/report-small.json の合成データだけにしてください。
変更ファイル、実行コマンド、最も遅い区間を返してください。
本番設定、billing、secret、customer data handling、deploy は人の承認なしに変えないでください。"

この段階では、直す範囲を広げないほうが速いです。計測コードを入れる、テストを回す、遅い順に出す。まずそれだけで十分です。

動く確認コード

コードは最小で構いません。Node.js公式の node:perf_hooks には performance.now() があり、追加packageなしでミリ秒を測れます。下の例は合成データだけを使います。secret、token、本番URL、顧客行は入っていません。

measured-report.mjs として保存し、ローカルで実行してください。

import { performance } from "node:perf_hooks";

const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

function createTimer() {
  const records = [];
  return {
    async measure(label, fn) {
      const start = performance.now();
      const value = await fn();
      records.push({ label, ms: Number((performance.now() - start).toFixed(1)) });
      return value;
    },
    report() {
      return records.sort((a, b) => b.ms - a.ms);
    },
  };
}

async function fetchUsers() {
  await wait(60);
  return Array.from({ length: 6 }, (_, index) => ({ id: index + 1 }));
}

async function fetchOrdersOneByOne(users) {
  const orders = [];
  for (const user of users) {
    await wait(45);
    orders.push({ userId: user.id, total: user.id * 10 });
  }
  return orders;
}

async function fetchOrdersInBatch(users) {
  await wait(55);
  return users.map((user) => ({ userId: user.id, total: user.id * 10 }));
}

function buildReport(users, orders) {
  const ordersByUser = new Map(orders.map((order) => [order.userId, order]));
  return users.map((user) => ({ ...user, orderTotal: ordersByUser.get(user.id)?.total ?? 0 }));
}

async function run(fetchOrders) {
  const timer = createTimer();
  const users = await timer.measure("users", fetchUsers);
  const orders = await timer.measure("orders", () => fetchOrders(users));
  await timer.measure("report", () => buildReport(users, orders));
  console.table(timer.report());
}

console.log("slow version");
await run(fetchOrdersOneByOne);

console.log("batch version");
await run(fetchOrdersInBatch);

実行コマンドです。

node measured-report.mjs
node --prof measured-report.mjs
node --prof-process isolate-*.log > profile.txt

node --prof は公式の Node.js CLI reference にある組み込みprofilerです。CPU負荷の高い関数が簡易timerだけで見えないときに使います。API待ちやCSV batchでは、まず小さな表で遅い区間を見つけるほうが早いです。

3つのUse case

API、CSV、テストという現場の作業ごとに、入力、出力、人の確認を分けます。Claude Codeへ渡す情報が同じでも、人が見るリスクは変わります。

Use case 1: APIレスポンスが遅い

入力: src/api/report.ts、route test、合成request payload。本番DBの接続文字列や顧客IDは渡さず、fixtureだけを使います。

出力: DB取得、外部API取得、変換、レスポンス整形のtime table。最も遅い区間を1つだけ修正するpatch。

人の確認: 認証、権限、顧客データの保存期間、rate limit、production deploy。Claude Codeが差分を作っても、この判断は人が見ます。

Use case 2: CSV exportのbatchが遅い

入力: fake rowsを入れたfixture、CSV生成コマンド、期待する列名。外部サービスへ送信するコードはこの段階で実行しません。

出力: row load、join、format、file writeの計測結果。N+1 read、直列API call、formatterの再生成が見えたら、その1箇所だけ直します。

人の確認: 本番CSV、個人情報、billingが発生するjob、送信先URL。合成データの結果をそのままproduction benchmarkとは呼びません。

Use case 3: release前のpull request review

入力: pull request diff、既存のperformance test、許容する応答時間の目安。曖昧なら「今のservice targetを悪化させない」と書きます。

出力: 実行したコマンド、測った区間、未測定のリスク、修正patch。レビューコメントには「見た感じ」ではなく、確認したfileとcommandを書きます。

人の確認: release判断、rollback、SLA、顧客向け説明。Claude Codeは候補を並べられますが、公開判断は人が持ちます。

測った後の直し方

テストとコマンドで遅い区間が出たら、次の表に当てはめます。表は推測の入口ではなく、計測後の整理用です。

計測で見えた症状よくある原因最初に試す直し方
同じ形のqueryやAPI callが大量に出るN+1IDをまとめて取得し、関連データを一括で読む
独立した処理の時間が足し算になる直列 await依存しない処理だけ Promise.all にする
同じformatや変換が何度も走る無駄な再計算Mapでmemoizeするか、setupをloop外へ出す
件数が増えると急に重い二重探索、index不足Mapを先に作る。DBならquery planを見てからindexを考える

Promise.all は魔法ではありません。依存関係がある処理を並列にすると壊れます。外部APIやDBへ無制限に投げるとrate limitや障害につながります。件数が多い処理では同時実行数の上限を入れてください。

Pitfall: 推測で直して確認を抜く

コマンドなしで「速くして」と頼むのが失敗の原因です。Claude Codeは速そうな書き換えを出せますが、遅い区間が特定されていないと、関係ないコードを直す可能性があります。直し方は、再現コマンド、fixture、計測表を先に要求することです。

合成データの数字を本番benchmarkのように扱うのも危険です。合成データは犯人候補を探すためのものです。本番の作業時間、SLA、売上への影響、問い合わせ率を語るなら、自社のobservabilityと承認済みのproduction dataが必要です。

もう1つの落とし穴は、直列loopを無制限の Promise.all に変えることです。原因は「並列なら速い」という単純化です。修正は、依存関係を確認し、DB、paid API、外部serviceには同時実行数の上限を置くことです。

Claude Codeに任せる範囲と人が見る範囲

コード、テスト、fixture、計測ログの整理はClaude Codeに任せやすい領域です。temporary timerを入れる、loop内queryを探す、batch化案を出す、memoizationのpatchを書く、実行コマンドと残リスクをまとめる。これらは差分で確認できます。

人が見る範囲は別です。secret、顧客データ、auth、billing、production deploy、外部serviceへの送信、SLAの主張は人が決めます。Claude Codeが「この設定も変えると速い」と提案しても、承認なしに進めません。

promptには次の一文を入れておくと事故が減ります。

Claude Code may edit local measurement code and tests. Human approval is required before using production data, changing secrets, changing billing controls, calling external services with real data, or deploying.

よくある質問

Q. まず入れる計測toolは何ですか? 最初は追加toolなしで構いません。performance.now()console.table で、どの区間が遅いか見ます。それでもCPU関数が見えないときだけ node --prof を使います。

Q. N+1かどうかはどこで見ますか? loopの中に await db.query(...) や外部API callがあり、同じ形のlogが行数ぶん出ていたら疑います。SQLならquery log、APIならrequest countを見ます。

Q. indexを足せば速くなりますか? query planを見てから決めます。indexはreadを助ける一方でwriteとstorageのコストを増やします。slow pathがDB以外なら、indexを足しても作業時間だけ消えます。

Q. Claude Codeに最初から修正まで任せていいですか? local fixtureとtestだけなら任せやすいです。本番データ、secret、billing、deployが絡むなら、人の承認を挟んでください。

主要CTA

この計測、patch、検証の流れをチームの共通手順にしたい場合は、Claude Codeの研修・相談 を見てください。狙いは「何%速くなる」という広告文ではなく、コマンド、fixture、測定したボトルネック、修正差分、残リスクを毎回残す運用です。

実際に試した結果

この記事で紹介した内容を実際に試した結果として、今回確認したのは記事内のコード、コマンド、リンク、frontmatter、CTAです。measured-report.mjs のJavaScriptは標準Node.jsだけで動く形にし、データは合成値だけにしました。内部リンクは日本語の /blog//training/、外部リンクはNode.js公式docにそろえました。

production benchmark、実顧客incident、実サービスの速度改善は確認していません。そのため「何分短縮」「何%改善」といった実運用の成果は書いていません。今日まずやるなら、遅いAPIかCSVを1つ選び、上のpromptを貼り、計測tableを出すところまで確認してください。

#Node.js #パフォーマンス #計測 #N+1 #プロファイリング
無料

無料PDF: Claude Code はじめてのチートシート

まずは無料PDFで基本コマンドと最初の使い方をまとめて確認してください。登録後はそのままテンプレート集や導入相談にも進めます。

スパムは送りません。登録情報は厳重に管理します。

Claude Codeを仕事で使える形にしませんか?

まず無料PDFで基本を固め、繰り返し使う作業はGumroad教材へ、チーム導入や権限設計は導入相談へ進めます。

Masa

この記事を書いた人

Masa

Claude Codeの実務活用、導入設計、収益導線改善を検証しているエンジニア。10言語の技術メディアを運営中。

PR

関連書籍・参考図書

この記事のテーマに関連する書籍を楽天ブックスで探せます。

※ 当サイトは楽天市場のアフィリエイトプログラムに参加しています。上記リンクから商品をご購入いただくと、運営者に紹介料が支払われる場合があります。