3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

package-lock.json をコミットしないと何がずれるか — npm install と npm ci で実測

3
Posted at

package-lock.json は、パッケージを1つ足すだけで数百行の差分が出ることがあります。そのため「自動で作られるファイルだし、差分がうるさいから .gitignore に入れてしまおう」と考える人もいます。

結論から書くと、アプリのリポジトリでは package-lock.json はコミットしたほうがいいと考えています。理由は、これがないと「自分の手元」と「ほかの人の手元・本番のビルド」で、入るパッケージのバージョンが変わりうるからです。この記事では、npm 11.11.0 で実際に試した結果をもとに、その理由と npm install / npm ci の使い分けを整理します。

package.json だけでは、入るバージョンは決まらない

package.json の依存は、たいてい ^ 付きで書かれます(抜粋)。

{
  "dependencies": {
    "ms": "^2.0.0"
  }
}

^2.0.0 は「2.0.0 以上、3.0.0 未満ならどれでもいい」という範囲の指定です。範囲なので、実際にどのバージョンが入るかは、インストールしたタイミングで変わりえます。

それを固定しているのが package-lock.json です。実際に入ったバージョンと、どこから取ってきたかが記録されています(抜粋)。

"node_modules/ms": {
  "version": "2.0.0",
  "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",

実際に試した結果

ms を 2.0.0 で入れたあと、package.json を "^2.0.0" に書き換えた状態で比べました。

状況 実行したコマンド 入ったバージョン
package-lock.json あり(2.0.0 が記録されている) npm install 2.0.0
package-lock.json だけ消した(node_modules は残っている) npm install 2.0.0
package-lock.json と node_modules を消した(clone 直後と同じ状態) npm install 2.1.3

手元では、ロックファイルを消しても node_modules に入っている版が使われ続けたので、違いに気づきにくいです。ところが node_modules もない状態、つまり別の人が clone した直後と同じ状態では、同じ package.json から 2.1.3 が入りました。

package-lock.json をコミットしていないと、ほかの人が git clone して npm install したときや、Vercel などで本番用に build したときに、範囲内でそのとき最新のバージョンが入ることがあります。手元では動くのに本番の build だけ失敗する、ということが起きたときに、原因の切り分けが難しくなります。

npm ci は「ロックファイルどおりに入れる」コマンド

npm ci は、package-lock.json に書かれたとおりに入れるためのコマンドです。npm install との違いを、実際に試した結果でまとめます。

状況 npm install npm ci
ロックファイルあり・package.json と一致 ロックどおりに入る ロックどおりに入る
package.json だけ書き換えた(ロックと不一致) ロックファイルを書き換えて入れる エラーで止まる
ロックファイルも node_modules もない 範囲内の最新を入れて、ロックファイルを作る エラーで止まる

package.json に手で "is-number": "^7.0.0" を足し、npm install をせずに npm ci を実行すると、こう言って止まりました。

npm error code EUSAGE
npm error `npm ci` can only install packages when your package.json and package-lock.json or npm-shrinkwrap.json are in sync. Please update your lock file with `npm install` before continuing.
npm error Missing: is-number@7.0.0 from lock file

npm install は「足りないものを足して、ロックファイルも直す」コマンドなので、食い違いをエラーにせずに埋めます。npm ci は「食い違いがあれば止まる」ので、ロックファイルの更新忘れに気づけます。

使い分けの目安

  • パッケージを足す・上げるとき → npm install <名前>、消すとき → npm uninstall <名前>(どちらもロックファイルが更新されるので、package.json と一緒にコミットする)
  • clone した直後や、ほかの人の変更を pull したあと → npm ci を使うと、記録どおりのバージョンがそろう
  • CI や本番の build → npm ci

なお npm ci は、実行のたびに node_modules を作り直すので、そのぶん毎回少し時間がかかります。package.json とロックファイルがそろっていれば、手元では普段 npm install を使っても記録どおりの版が入ります(上の表の1行目)。

ライブラリの場合は事情が違う

npm に公開する「ライブラリ」を作っている場合は事情が違います。ライブラリを使う側には、ライブラリの package-lock.json は配られないので、利用者の手元での動きを固定する役には立ちません。ただ、ライブラリでも開発する人どうしや CI の環境をそろえる目的でコミットすることはあり、方針が分かれます。この記事の話は、Webアプリのように「そのリポジトリ自体を動かす」場合の話です。

AI に依存関係を触らせるときの確認ポイント

AI が npm install <名前> を実行すれば、package.json と package-lock.json の両方が書き換わります。ただし package.json を直接書き換えただけだと、ロックファイルは古いままです(さっき npm ci が止まった状態)。ロックファイルの差分は大きいので見るのを飛ばしたくなりますが、次の点だけ確認しておくと安心です。

  • package.json の変更と、package-lock.json の変更が、同じコミットに入っているか
  • 頼んでいないパッケージの指定(バージョンの範囲)まで書き換わっていないか(git diff package.json で確認できる)
  • 手元で npm ci が通るか(ロックファイルの更新漏れがあれば、ここで止まる)

まとめ

  • package.json の ^ は範囲指定なので、それだけでは入るバージョンが決まらない
  • package-lock.json があると、記録どおりのバージョンが入る(試した例では 2.0.0 のまま。ロックファイルと node_modules を消すと 2.1.3 が入った)
  • npm ci はロックファイルどおりに入れ、食い違いやロックファイルがないときは止まる
  • アプリのリポジトリでは、差分がうるさくても package-lock.json はコミットする

未経験者向けの講座を運営しています

未経験から Next.js + Supabase + Claude Code で Webアプリを公開するまで を、全24セッションで体系化した教材です。Claude Code を学習パートナーにする CLAUDE.md と学習モード(learner / developer)の設計までセットで含みます。

※ 中上級者には易しすぎる内容なので、初心者の知り合いへの紹介や社内研修の参考としてどうぞ。

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?