DevToolBox

APIレスポンスの差分を正しく比較する

リリース前後の回帰確認やステージング/本番の環境間比較で、APIレスポンスをテキストdiffにかけるとキー順や整形の違いだけで大量の「偽の差分」が出てしまいます。 本記事では、構造比較(セマンティック diff)の考え方と、タイムスタンプ等の揺らぐフィールドの扱い、 そして jq による正規化テクニックを、コピペで使えるコマンド例つきで解説します。

Plain text diff produces false positives on JSON because key order and formatting are not significant. This guide shows how to compare API responses structurally, how to exclude volatile fields such as timestamps and request IDs, and how to normalize payloads with jq before diffing.

TL;DR

JSON Diff

2つのJSONを構造で比較。キー順や整形の違いを無視し、追加・削除・変更されたパスだけをハイライトします。処理はブラウザ内で完結し、データは外部送信されません。

今すぐ試す →

1. テキストdiffが誤検出を生む理由 / Why text diff fails on JSON

次の2つのレスポンスはデータとして完全に同一ですが、テキストdiffでは全行が差分になります。

# 旧環境: minify済み・キー順はDB由来
{"age":30,"name":"Tanaka","tags":["a","b"]}

# 新環境: pretty print・キー順はシリアライザ由来
{
  "name": "Tanaka",
  "tags": ["a", "b"],
  "age": 30
}

JSONの仕様(RFC 8259)ではオブジェクトのメンバー順序に意味はなく、空白・改行も自由です。 ライブラリ更新やDBのクエリプラン変更でキー順が入れ替わるのは正常な挙動であり、 行単位のdiffで検出されるのは「シリアライズの癖」であってAPIの変更ではありません。 誤検出が数百行も出ると、その中に紛れた本物の差分(値の変更・フィールドの消失)を見逃します。

Per RFC 8259, object member order and whitespace are insignificant. Line-based diff flags serialization quirks, burying the one real change you actually need to catch.

2. 構造比較(セマンティック diff)の利点 / Structural diff

構造比較は両方のJSONをパースしてから値同士を比較します。キー順・整形・エスケープ表現の違いは 消え、結果は「どのパスが追加/削除/変更されたか」というリストになります。

変更: user.plan        "free"  → "pro"
削除: user.trial_ends  "2026-06-30"
追加: user.billing.cycle "monthly"

注意点は配列の扱いです。JSONの配列は順序付きなので、要素の並び替えは仕様上「差分」です。 ただし検索結果やタグ一覧など、APIが順序を保証しないエンドポイントでは、 比較前に id などの安定キーでソートして正規化するのが定石です(後述の sort_by)。 順序が契約の一部(ランキングAPIなど)なら、正規化せずそのまま比較してください。

A structural diff parses both documents and reports added/removed/changed paths. Arrays are ordered by spec — sort them by a stable key only when the API does not guarantee order.

3. 無視すべき「揺らぐフィールド」の扱い / Volatile fields

正しく構造比較しても、リクエストのたびに変わるフィールドは毎回差分になります。 代表的なものと扱いを整理します。

フィールド例 / Field変わる理由 / Why it changes扱い / Treatment
timestamp, created_at, updated_at生成時刻 / Generated time削除 or 固定値に置換 / Delete or pin
requestId, traceIdリクエスト毎に採番 / Per-request ID削除 / Delete
id, uuid(新規作成リソース)環境ごとに別採番 / Env-specific環境間比較では削除 / Delete for cross-env
token, signature認証・署名 / Auth material削除。共有前に必ずマスク / Delete & mask
version, buildデプロイ情報 / Deploy info変更を確認したいなら残す / Keep if relevant

重要なのは「無視リストを比較スクリプトに明文化しておく」ことです。 その場しのぎで目視スキップすると、次回の比較で同じノイズに時間を取られます。 また、本番レスポンスにはトークンや個人情報が含まれがちです。比較結果をチケットやチャットに 貼る前に、該当フィールドを削除・マスクしてください。

Codify the ignore list in your comparison script instead of skipping noise by eye. Strip tokens and PII before sharing diff output anywhere.

4. jq での正規化テクニック / Normalizing with jq

CLIで完結させたい場合は jq で正規化してから diff にかけます。-S(--sort-keys)がキー順問題を、再シリアライズが整形差を解決します。

# キーを辞書順にソートし、整形を統一する
jq -S . response.json

# 揺らぐフィールドをトップレベル/特定パスから削除
jq -S 'del(.requestId, .meta.timestamp)' response.json

# ネストの深い updated_at を再帰的に全削除
jq -S 'walk(if type == "object" then del(.updated_at) else . end)' response.json

# 順序を保証しない配列を安定キーでソート
jq -S '.items |= sort_by(.id)' response.json

リリース前後の回帰確認は、正規化フィルタを1つの変数にまとめて両方に適用するのがポイントです。

curl -s https://staging.example.com/v1/users -o new.json
curl -s https://prod.example.com/v1/users    -o old.json

NORM='del(.requestId, .meta.timestamp) | .items |= sort_by(.id)'
diff <(jq -S "$NORM" old.json) <(jq -S "$NORM" new.json)

# diff の終了コード: 0=差分なし, 1=差分あり, 2=エラー
# → CI ではこの終了コードをそのまま合否判定に使える

片方だけにフィルタをかけると、削除したフィールド自体が差分として出てしまいます。 必ず同じ正規化を両方のレスポンスに適用してください。 GUIで確認したいときは、正規化後のJSONを JSON Diff に貼れば 変更パスがハイライト表示されます。

Put the normalization filter in one variable and apply it to both payloads, then rely on diff's exit code (0 = identical) for CI gating. Applying the filter to only one side creates artificial differences.

5. まとめ / Summary

To compare API responses reliably, parse first and compare structure, not text. JSON key order and whitespace carry no meaning, so a line diff mostly reports serialization noise. Normalize both payloads the same way: sort keys with jq -S, delete volatile fields such as timestamps and request IDs with del() or walk(), and sort order-insensitive arrays with sort_by(). Then a plain diff — or a structural JSON diff tool — shows only genuine regressions, and its exit code plugs straight into CI.

JSON Diff

正規化後のレスポンスを貼るだけで、追加・削除・変更されたパスを色分け表示。リリース前後の最終確認に。

今すぐ試す →

関連ツール / Related tools

関連ガイド / Related guides