3秒でわかる
開発中だけ必要なパッケージを分けて記録する欄。本番の成果物に混ぜないことで、配布物のサイズと攻撃を受ける面を小さく保てます。
もう少し詳しく
どういうものか
devDependencies は npm の package.json にある欄で、開発作業でだけ使うパッケージを記録します。テストツール、型定義、リンタ、ビルドツール、開発サーバーなどが該当します。
対になるのが dependencies で、こちらはアプリを本番で動かすのに必要なものです。判断の基準は「本番で実行されるコードから import されるか」です。import されるなら dependencies、コマンドや設定からしか使わないなら devDependencies に入ります。
なぜ必要か
本番環境では npm ci --omit=dev のように開発用を除いて導入できます。ライブラリの数が減れば、コンテナイメージは小さくなり、起動も速くなります。
セキュリティ上の意味も大きいです。導入したパッケージはさらに依存を引き連れてくるため、数百のパッケージが入るのは珍しくありません。本番に置かなければ、そこに脆弱性が見つかっても本番の攻撃対象にはなりません。
ライブラリとして公開する場合はさらに重要です。dependencies に書いたものは利用者の環境にも入るため、テストツールをここに書くと利用者全員に不要な荷物を配ることになります。
具体例
{
"<a href="/glossary/dependencies" class="text-primary font-medium underline underline-offset-2 hover:text-primary-dark">dependencies</a>": {
"express": "^4.19.2",
"zod": "^3.23.8"
},
"devDependencies": {
"typescript": "^5.5.4",
"vitest": "^2.0.5",
"@types/express": "^4.17.21"
}
}追加するときにフラグで振り分けます。
npm install express # dependencies に入る
npm install -D vitest # devDependencies に入る
npm ci --omit=dev # 本番用。開発用を除いて導入するつまずきやすいところ
TypeScript のプロジェクトで多いのが、@types/* を devDependencies に置いたまま本番でビルドしようとして失敗する事故です。ビルドを本番環境で行うなら型定義も必要になります。ビルド済みの成果物だけを本番へ運ぶ構成にすると、この矛盾は起きません。
逆に、本番コードが import するライブラリを -D で入れてしまい、ローカルでは動くのにデプロイ後に見つからないというエラーも定番です。どちらの欄に入ったかは追加直後に確認します。
なお、この区別は導入時だけの話で、npm audit の対象や lock ファイルには両方が載ります。
覚え方
「本番のコードが import するか」だけで振り分けます。