Node のサービス向け
どの関数、どのクエリ、どのデプロイか。
コマンド1つで入るエージェントが、エクスポートされたすべての非同期関数と、アプリが出すすべてのクエリを測ります — そして、どの関数がどのクエリを出したかを知っています。リリースは毎回1つ前と比較されるので、検出結果は時刻ではなくあなたのデプロイを名指しします。
何を測るか
- エクスポートされたすべての非同期関数
- どれだけ時間がかかり、そのうちどれだけが自分自身のコードで、どれだけが呼び出した先だったか。「遅くなったのは自分のコードか、呼んだ先か」が、午後ひとつではなく数値ひとつになります。
- 5つのクライアントにわたる全クエリ
- pg、mysql2、postgres.js、MongoDB のドライバ、Prisma — 自動で見つけて計測します。Mongoose 越しでも、負荷のかかったコネクションプール越しでも。返却行数、カラム数、レスポンスサイズ、そして MongoDB については1回の読み取りが実際に何往復のネットワークを必要としたか。
- どの関数がどのクエリを出したか
- 残りを読む価値のあるものにしている部分です。クエリはその上にある関数に帰属するので、N+1 はループを持っている関数の上に出ます。いつも問題なく見えていたクエリの側に散らばりません。
- データベースが答えるために何をしたか
- Postgres には各文のプランを尋ねます — パラメータを一度も束縛せずに、なので、あなたの値は一切関わりません。MySQL は代わりに performance_schema から読みます。返却行あたりの走査行数と、そもそもインデックスが使われたかどうか。どちらも同じ検出として届きます。「これはそのテーブルをインデックスなしで読んでいる」と。
- あなたの HTTP ルート、受けと出し
- 処理したリクエストと、他人の API を待っていた時間 — 遅いサードパーティが「あなたのコードが遅い」として報告されなくなります。
何を見つけるか
どれも1つ前のリリースと比較されます。だから検出結果は、日付ではなく変更を名指しします。
前のリリースにはなかった N+1
1リクエストにつき1回だったデータベース呼び出しが、いまは14回。デプロイに紐づいているので、それを持ち込んだリリースを名指しします。
テーブル全体を読み始めたクエリ
1回あたりの行数が30から30,000へ。ステージング規模のデータと温まったデータベースが相手ならほとんど時計は動きません — そして1週間後に本番を落とします。
インデックスを使わなくなったクエリ
ここで唯一、症状ではなく原因を名指しする検出です。まだ何も遅くないうちに届くことがよくあります。テーブルがまだ小さくてスキャンでも問題ない段階で、そして数週間後、小さくなくなったときに障害になります。
自分のコードが遅くなっている
呼び出した先ではなく関数自身の中で使われた時間 — 分けてあるので、1回目で正しいファイルを開けます。
数百回のネットワーク待ちに化けた読み取り
MongoDB はカーソルをバッチで返します。結果が1バッチを超えると、1回の読み取りが数十回〜数百回の連続した往復になります — ドキュメントは同じなので、システムの中で動くのは時計だけです。
いまはループの中で呼ばれているもの
1回あたりは変わらず — 同じ速さ、同じデータ — それでも、以前は1回だった呼び出しが数百回になっています。
静かに何も返さなくなったクエリ
成功し、エラーもなく、遅くもなく、以前は行が返っていたのに空で返ってきた。この種のサービスが壊れる最も一般的な形は、数字を大きくではなく小さくします。
フィードに何が届くか
読むためのダッシュボードではありません。検出1件につきカード1枚。数値と、それを変えたリリースと、コーディングエージェントが修正を書くのに必要な証拠が付いています。
これは `pnpm e2e` が出力する数値で、誰がこのリポジトリをクローンしても同じです。サービスを用意し、本物の回帰を2つ含んだリリースを出し、それを検出します — つまりここに私たちが選んだ数字は1つもありません。
orders.getUserOrdersリファクタリングの途中で WHERE 句が消え、関数はテーブル全体を取ってきて JavaScript で絞り込んでいます。温まったデータベースでは、時計はほとんど動きませんでした。
リリース v2 以降、v1 との比較
orders.enrichOrdersクエリがループの中に移りました。どれも速いので、1つのリクエストとして見ればおかしなところはありません — 変わったのは回数だけです。
リリース v2 以降、v1 との比較
導入方法
すでに開いているコーディングエージェントに渡してください。リポジトリを読み、それが Next の設定なのか、Vite プラグインなのか、起動コマンドなのかを判断して組み込みます — そして同じ会話の中で、見つかったものを直します。
claude mcp add sixty \
-e SIXTY_API_KEY=sixty_sk_… \
-e SIXTY_ENDPOINT=https://ingest.sixty.sh \
-- npx -y @sixty-sh/mcp
# or by hand, in your server entry:
import { init } from '@sixty-sh/node'
init()リリース識別子は Vercel、Render、Railway、Fly、Heroku、GitHub Actions から自動で拾われます。最初の比較は次のデプロイのあとに届きます。1つのリリースには比べる相手がないからです。
できないこと
- MySQL と MongoDB はプランのツリーを返しません。MySQL の EXPLAIN には実際のパラメータ値が、MongoDB の explain には実際のフィルタが必要で、このエージェントは値を見た瞬間に捨てます — なので MySQL では代わりに performance_schema から結論を読み、MongoDB についてはまだ何もありません。
- 非同期ジェネレータはビルド時の変換が飛ばします。包んでしまうと「呼び出し側がイテレータを受け取るまでの時間」を測ることになり、それは誰も欲しくない数値だからです。
- エージェントが測るのは JavaScript から届く範囲です。ネイティブモジュールの中や別プロセスで使われた時間は、待ち時間としてしか見えません。
これが理由でここに来たのなら
最後の変更が何をしたのか、確かめてください。
試してみる別のものを作っていますか? Python のサービス、Lovable のアプリ、React Native のアプリ のページもあります — 見えるものが違うので、内容も違います。