Claude Codeで依存のメジャーアップデートを調べる — changelogと呼び出し箇所の突き合わせ
npm-check-updatesでメジャー更新の候補を絞り、changelogをclaude -pに渡して、自分のコードに触れる破壊的変更だけを表にする手順です。
依存パッケージのメジャーアップデートは、上げる前に「どの破壊的変更が自分のコードに当たるか」を調べる作業が重い部分です。changelogには十数項目の破壊的変更が並びますが、自分のリポジトリに効くのはそのうち数項目、ということがよくあります。この記事は、候補の抽出をnpm-check-updates(ncu)に、changelogと呼び出し箇所の突き合わせをClaude Codeに任せ、結果を表にして人が判断する流れを書きます。
ここで作るのは、更新する前の影響調査です。更新PRを自動で作って検証する運用はRenovateとDependabotのPRを検証する記事に、Jestのような個別パッケージの移行はJest 30への移行手順にあります。
更新候補を「上げてよいもの」と「読むもの」に分ける
全部のパッケージのchangelogを読む必要はありません。semverに従うパッケージなら、patchとminorは互換性が保たれる前提で上げ、読むのはメジャー更新だけに絞れます。ただし例外が1つあります。後述するとおり、0.x系はminorでも壊れることがあるので、読む側に入れます。
npm outdatedは、この切り分けの根拠になる列を持っています。Wantedはpackage.jsonの範囲に収まる最新のバージョン、Latestはレジストリでlatestタグが付いたバージョンです。LatestがWantedより先に進んでいれば、範囲の外に更新があるということです。同じページの注記では、範囲より上の新版(多くはメジャー、または0.xのminor)が黄色で示されると説明されています。
ncuはpackage.jsonの範囲を無視して最新版を探し、^18.3.1のような範囲の書き方は保ったまま書き換える候補を出します。package.jsonだけを書き換え、ロックファイルとnode_modulesはnpm installまで触りません。調べるだけの段階では、-uを付けずに候補を見るのが安全です。
手順の全体像
影響調査の5段階
- 1
作業ブランチを切る
調査の成果物を置く
.upgrade-survey/を作り、本体のコードを触らない状態から始めます。 - 2
メジャー更新の候補だけを抜く
ncuを2回実行し、差分からメジャー更新のあるパッケージを取り出します。 - 3
changelogをファイルに置く
候補ごとに、現在のバージョンから更新先のバージョンまでの変更履歴を保存します。
- 4
claude -pで突き合わせる
changelogを標準入力で渡し、呼び出し箇所の検索結果をJSONで受け取ります。
- 5
表にして人が判断する
「該当あり」と「人が見る」の項目だけを読み、更新の順番と範囲を決めます。
最初の候補洗い出しだけは、claude --permission-mode planで始めるのが無難です。Plan modeのClaudeはファイルを読み、シェルコマンドで調べますが、ソースは編集しません。調査の途中で意図せずコードが書き換わる心配を、起動の時点で外せます。
メジャー更新だけを取り出す
ncuは更新先の水準を--targetで選べます。--target minorならpatchとminorまでにとどめ、メジャーは候補から外します。この性質を使い、「全部上げた場合」と「minorまでの場合」の差を取ると、メジャー更新が残ります。
git switch -c chore/major-update-survey
mkdir -p .upgrade-survey/changelog
npx npm-check-updates --jsonUpgraded > .upgrade-survey/all.json
npx npm-check-updates --target minor --jsonUpgraded \
> .upgrade-survey/safe.json
jq -n --slurpfile a .upgrade-survey/all.json \
--slurpfile b .upgrade-survey/safe.json \
'$a[0] | to_entries
| map(select(.value != ($b[0][.key]))) | map(.key)' \
> .upgrade-survey/majors.json--jsonUpgradedは、更新先の依存だけをJSONで出すオプションです。all.jsonには全更新が、safe.jsonにはメジャーを除いた更新が入ります。safe.jsonに同じ値が無い、または値が違うパッケージは、メジャー更新が控えているものです。majors.jsonにはそのパッケージ名の配列ができます。
jqの式は、all.jsonの各キーについてsafe.jsonの値と比べています。safe.jsonに無いキーはnullと比べるので、メジャーしか候補が無いパッケージも拾えます。
候補が多いときは、ncu --format groupで更新の種別ごとにまとまった表示を見るのも手です。--format repoを付けるとソースリポジトリへのリンクが出ますが、READMEによると、この表示にはパッケージがインストール済みであることが必要です。
changelogは標準入力で渡し、記憶で答えさせない
ここが、この手順でいちばん効く設計です。「ReactをNに上げると何が壊れる?」と聞けば、Claudeは学習済みの知識で答えますが、その知識が今回の更新先のバージョンまで届いている保証はありません。そこで、現在のバージョンから更新先までの変更履歴を自分で用意し、標準入力で渡して、判定の根拠をそのテキストに限ります。
changelogの保存方法は問いません。リポジトリのCHANGELOG.md、リリースページのコピー、更新後のパッケージに変更履歴が同梱されていればそのファイルなど、手元で用意しやすいものを使います。保存する範囲は、現在のバージョンより後から更新先のバージョンまでです。範囲が広すぎるとコンテキストを食い、狭すぎると途中のメジャーを見落とします。たとえば^3.2.0から^5.1.0へ上げるなら、4.0.0と5.0.0の両方の節が要ります。
渡す指示は、CLAUDE.mdに調査用の約束として書いておくと、毎回の指示を短くできます。
## 依存の更新調査
- 調査では package.json・ロックファイル・ソースを書き換えない
- 判定は標準入力で渡された変更履歴の記載だけを根拠にし、記憶で補わない
- 検索対象はソース、設定ファイル、package.jsonのscripts、CIのworkflow
- 見つからなかった項目は「該当なし」とだけ書き、安全とは書かない最後の1行が重要です。検索で見つからないことは、影響が無いことの証明にならないためです。詳しくは後の検索範囲の節で書きます。
結果を型のそろったJSONで受け取る
パッケージが何十個もあると、自由な文章で返ってくる報告は読み比べにくくなります。claude -pは--output-format jsonと--json-schemaを併せて使うと、スキーマに沿った結果がstructured_outputフィールドに入ります。スキーマにはJSON Schemaを渡します。
{
"type": "object",
"properties": {
"package": { "type": "string" },
"breaking": {
"type": "array",
"items": {
"type": "object",
"properties": {
"change": { "type": "string" },
"search": { "type": "string" },
"hits": { "type": "array", "items": { "type": "string" } },
"verdict": {
"type": "string",
"enum": ["affected", "not-found", "needs-human"]
}
},
"required": ["change", "search", "hits", "verdict"]
}
}
},
"required": ["package", "breaking"]
}これを.upgrade-survey/schema.jsonに保存し、パッケージごとに実行します。権限はdontAskにして、読み取り系のツールだけを許可します。dontAskは承認が必要な呼び出しをすべて拒否するモードで、作業ディレクトリ内のファイル読み取りや読み取り専用コマンドは承認なしで動くため、書き込みを締めた状態で調べられます。
pkg=pkg-a # 調べるパッケージ名(例)
cat ".upgrade-survey/changelog/$pkg.md" | claude -p \
"標準入力は $pkg の変更履歴。破壊的変更を1項目ずつ挙げ、各項目が
このリポジトリに影響するかを検索して判定して" \
--permission-mode dontAsk \
--allowedTools "Read,Grep,Glob" \
--output-format json \
--json-schema "$(cat .upgrade-survey/schema.json)" \
| jq -r '.structured_output | .package as $p | .breaking[]
| [$p, .verdict, .change, (.hits | join(";"))] | @tsv'出力は、パッケージ名・判定・変更の内容・見つかった場所をタブで区切った1行ずつの形です。全パッケージ分を1つのファイルに追記すれば、そのまま表計算ソフトに貼れます。例えば次のような形になります(架空のパッケージの例です)。
| パッケージ | 判定 | 破壊的変更 | 見つかった場所 |
|---|---|---|---|
| pkg-a | 判定affected | 破壊的変更init()の引数が必須に | 見つかった場所src/boot.ts:12 |
| pkg-a | 判定not-found | 破壊的変更CommonJS出力の廃止 | 見つかった場所(なし) |
| pkg-b | 判定needs-human | 破壊的変更対応Nodeが22以上に | 見つかった場所.github/workflows/ci.yml |
非対話実行の権限まわりは、claude -pの権限と--bareの解説に詳しくあります。
「該当なし」を安全と読まないための検索範囲
not-foundは「検索した範囲では見つからなかった」だけを意味します。安全の保証ではありません。次の取りこぼしが起こりやすい場所を、CLAUDE.mdの検索対象に入れておきます。
- 設定ファイルのキー: 廃止されたオプションは、コードでなく
*.config.jsやYAMLに書かれていることが多い - CLIのフラグ:
package.jsonのscriptsやCIのworkflowで呼ぶコマンドの引数が、メジャー更新で変わることがある - 動的な参照: 文字列で組み立てたimportや、プロパティ名を変数で渡す呼び出しは、単純な検索では当たらない
- 周辺の環境条件: 対応するNodeのバージョンの引き上げのように、呼び出しではなく実行環境に効く変更
最後の種類はneeds-humanに倒すのがよい項目です。コードを検索しても答えが出ないので、not-foundと返ってくると誤解を招きます。スキーマで3値に分けておくのはこのためです。
Claudeの検索はあくまで入口で、affectedの根拠になった行は、人が開いて該当の呼び出しを確かめます。変更内容の理解が浅いと、無関係な行を拾うこともあります。呼び出し側の洗い出しが主題になる別の場面は、OpenAPI定義の変更を扱うoasdiffで破壊的変更を検出する記事や、脆弱性の影響調査を扱うCVEの影響調査の記事が参考になります。
ncu --doctorとの使い分け
ncuには--doctorがあります。ncu --doctor -uは、まずテストが通ることを確かめ、全部を上げてテストを回し、落ちたら1つずつ上げ直して、壊れる更新を特定します。壊れない更新だけをpackage.jsonに残す、という動きです。-uが必須で、package.json・ロックファイル・node_modulesを書き換えるので、作業ブランチで実行します。
changelog調査との違いは、見つかる破壊的変更の種類です。
| 観点 | changelog突き合わせ | ncu --doctor -u |
|---|---|---|
| 更新前にコードを書き換えるか | changelog突き合わせ書き換えない | ncu --doctor -u書き換える |
| 見つかるもの | changelog突き合わせchangelogに書かれた変更のうち、呼び出しに当たるもの | ncu --doctor -uテストが落ちる更新 |
| 見落としやすいもの | changelog突き合わせ動的な参照、changelogに載らない変更 | ncu --doctor -uテストが通る範囲の外にある変更 |
| 向く場面 | changelog突き合わせ更新するか・順番をどうするかを決める前 | ncu --doctor -u更新したあとの裏取り |
2つは補い合います。テストが薄い領域の破壊的変更は--doctorでは拾えず、changelogに載らない挙動変更は突き合わせでは拾えません。先に突き合わせでaffectedを潰し、そのうえで--doctorを回すと、落ちた更新の原因を切り分けやすくなります。
0.x系と範囲外の更新で起きやすいつまずき
minorのつもりが壊れる
ncu --target minorは、0.x系のminorも更新対象にします。READMEの例では0.1.0が0.2.1に上がります。semverの規則では1.0.0より前は何が変わってもよいので、このminor更新はメジャー更新に相当します。READMEも、0.xの更新はインタラクティブモードの事前選択でもallのときだけ選ばれると書いています。
差分でメジャーを抜く手順は、0.xのminor更新を「安全」の側に入れてしまいます。safe.jsonのうち0.で始まる範囲の更新は、別に抜き出してmajors.jsonに足します。
範囲の上限付きの指定
^9.5.0 <10のように上限を書いている依存は、--target semverでは上限を超えて更新されません。上限を付けた理由が残っているパッケージなので、その理由(バージョンを止めた経緯)を読んでから、上限を外す更新を候補に入れます。
changelogが長すぎる
数メジャー分を一度に渡すと、読み落としが起こりやすくなります。更新先を一段ずつ分け、3→4の節と4→5の節を別々に判定すると、どのバージョンの変更が自分のコードに効くのかがはっきりします。更新の順番を決める材料にもなります。
まとめ
メジャー更新の影響調査は、changelogの理解、自分のコードの検索、結果の整理という3つの作業に分かれます。根拠にするテキストの用意を人が受け持ち、読み取り・検索・整理をclaude -pに任せれば、判定がClaudeの記憶に引きずられません。not-foundを安全と読まず、needs-humanの枠を残しておくことが、この手順で見落としを減らす要点です。
実際に更新へ進む段階では、affectedの項目から順に直し、ncu --doctorで裏を取ります。調査の表がそのままPRの説明にもなります。.upgrade-survey/は調査用の作業ディレクトリなので、コミットに入れる前に.gitignoreへ入れるか、結果の表だけをPRの本文に貼ります。
参照した一次ソース
この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。