開発ツール

dependenciesとdevDependencies、package-lock.jsonの役割

読了 6分
開発ツール

dependenciesとdevDependencies、package-lock.jsonの役割

npm install は毎日使っているのに、package.jsonpackage-lock.json が実際に何を管理しているのか、自分でも意外と曖昧なままだったので、調べたときのメモです。

依存の種類、バージョン範囲の記号、lockファイルの役割を押さえておくと、「なぜか動かない」をだいぶ減らせるみたいです。

まず dependencies と devDependencies の違い

package.json には依存の置き場所が2つあります。

{
  "dependencies": {
    "react": "^19.0.0"
  },
  "devDependencies": {
    "typescript": "^5.7.0",
    "prettier": "^3.4.0"
  }
}

違いは「アプリの実行時に必要かどうか」です。

dependenciesdevDependencies
内容実行時に必要なもの開発・ビルド時だけ必要なもの
react, nexttypescript, eslint, prettier
インストールコマンドnpm install パッケージ名npm install -D パッケージ名

ライブラリ(パッケージ)を作って公開する場合、この区別は特に重要です。利用者に入るのは dependencies だけで、devDependencies は入りません。テストツールを誤って dependencies に入れると、利用者に不要なものまで配ってしまうので、ここは注意です。

一方、アプリ(最終成果物)の開発では、ビルドツールでバンドルするので実行時の区別はあいまいになりますが、意図を表すドキュメント として正しく分けておくと、依存の見通しがよくなります。

バージョン範囲の記号

バージョン指定の先頭に付く記号は、許容する更新の幅を表します。npmはセマンティックバージョニング(メジャー.マイナー.パッチ)に従います。

記号許容範囲
^^1.2.31.x.x(メジャー据え置き、マイナー以下は上げる)
~~1.2.31.2.x(パッチのみ上げる)
なし1.2.3その版に固定

既定の ^ は「破壊的変更(メジャー更新)はしないが、機能追加やバグ修正は取り込む」という意味です。npm install のとき、この範囲内で最新のものが選ばれます。

なぜpackage-lock.jsonが要るのか

ここで問題になるのが、^1.2.3 のような 範囲指定だと、いつインストールするかで実際の版が変わる ことです。今日は 1.2.3、来週には 1.4.0 が入るかもしれません。これだと「自分の環境では動くのに、同僚やCIでは違う版で動く」という再現性の問題が起きます。地味にこれでハマったことがあります。

package-lock.json はこれを解決します。実際にインストールされた すべての依存(依存の依存まで含めて)の正確な版とハッシュ を記録して、ツリー全体を固定します。

{
  "node_modules/react": {
    "version": "19.0.0",
    "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz",
    "integrity": "sha512-..."
  }
}

このファイルがあれば、誰がいつ npm install しても まったく同じ依存ツリー が再現されます。なので package-lock.json は必ずGitにコミットします。たまに .gitignore に入れてしまう例を見ますが、これはやらないほうがよさそうです。

installとciの使い分け

クリーンな環境(CIなど)では npm install ではなく npm ci を使うのが定石のようです。

  • npm installpackage.json の範囲を満たすよう解決して、必要なら package-lock.json を更新する
  • npm cipackage-lock.json完全に従って インストールする。lockと矛盾があれば失敗する

npm cinode_modules を作り直してロックどおりに入れるので、再現性が高くて速い です。CIやデプロイでは npm ci、依存を増やす作業中は npm install、と使い分けています。

まとめ

  • dependencies は実行時に必要なもの、devDependencies は開発時だけのもの
  • ^ はメジャー据え置き、~ はパッチのみ、記号なしは固定
  • package-lock.json は依存ツリー全体の正確な版を固定して再現性を保証する。必ずコミットする
  • クリーン環境では npm ci でロックに忠実に入れる

範囲指定の細かい記法はnpm公式のpackage.jsonドキュメントで確認できます。「動かないのは依存の版違いかも」と疑えるようになると、トラブルの切り分けがぐっと速くなりました。以上です。