結構前に Node における NODE_ENV と環境変数の設定方法について Slack で軽く議論したんだけど、その後色々と考えるものがあったのでここにまとめておく。

基本的にサーバサイドを前提とした話をするが、フロントエンドにも通じる話だと思うし、Next.js の話も出てくる。

NODE_ENVdevelopmentproduction 以外が入るややこしさ

Node のエコシステムでよく使われる環境変数として NODE_ENV がある。

様々なツールが NODE_ENV を見て、developmentであれば開発用のトレースやログを出してくれるし、production であれば本番用に最適化された処理を行ってくれる。

これはこれで便利なんだけど、環境 (ENV) という言葉に釣られて、デプロイ環境の名称を NODE_ENV に設定し始めると、途端にややこしくなることが多い。

よくある例として、ステージング環境の NODE_ENVstaging に設定する例を考える。

このとき、あなた使っているビルドツールやライブラリが developmentproduction のどちらの挙動をするか正しく把握しているだろうか? 個人的には development の設定が優先されそう、って直感的に思うけど、確証はない。また、これはライブラリごとに挙動が統一されている保証がないので、それぞれ個別に調べる必要がある。

また、「ステージング環境だけど、本番環境と同じ環境にしたいからプロダクションビルドして欲しい」みたいな要望があると、ステージング環境なのに NODE_ENV=production を設定するという歪みが生じて辛くなる。

こういった歪みを考えていくと、NODE_ENV にデプロイ環境の名前をつけるの辞めたほうが良さそうに思える。 基本的に NODE_ENVdevelopmentproduction の 2 値しか取らないように設計し、より多様性を持たせたいものは NODE_ENV によらない形(それ専用の環境変数など)で切り替えられるようにすると良い。

NODE_ENV と環境変数

NODE_ENV を 2 値に絞るにあたって、「そういえばなんで NODE_ENV にいろんな値を入れてたんだっけ?」と改めて考えると、「設定値の変更」のためにやっているパターンが多い。

例えば、Node の設定ファイルの管理として有名で頻繁に使っている node-config は NODE_ENV を見て、 development.jsonproduction.ts といった設定ファイルを読み込む。 また、Next.js であれば NODE_ENV を見て .env.development.env.production を読み込む。

しかし、NODE_ENV が 2 値しか取らないように制限をかけると、2種類のファイルからしか読み込めないので、ステージング環境や QA 環境としった環境が新たに増えると、この方法では破綻してしまう。

そこで、ランタイムで設定されている環境変数から全ての設定を読み込むようにする。12 Factor App にも書かれているやつ。1

このように環境変数から読み込むようにすることで、設定の管理が NODE_ENV と分離される。 結果として、 NODE_ENV が 2 値しか取らなくても問題なくスケールする。

余談: 環境変数を .env から読み込む是非

環境変数をセットする方法として、 .env を使ってる人は多いと思う。全ての環境変数を手動でランタイムにセットするのはだるいから、 .env に書いて direnv や シェルスクリプトで自動で読み込むパターン。

まあそれでも良いっちゃ良いんだけど、ファイルに書くのがダメだから環境変数を使ってるのに、結局 .env というファイルを使ってしまってるところがもどかしい。

個人的には、アプリケーションの外部で .env を読み込むのであれば特に問題ないと思っている。アプリケーション内部から見たときには上で書いたとおり、環境変数で切り替わるような設定になっていてほしいが、どのように環境変数をランタイムに設定するかは特に気にしていない。direnv で読み込もうが、GCPのコンソールから設定しようが、コードをいじる必要がなければそれで十分という考え。

この辺はいろんな人の意見が聞きたい。

まとめ

言いたかったことは、次の2点。

  • NODE_ENVdevelopmentproduction の 2 値しか取らないようにしたほうが良くない?
  • .env は使ってもいいけど、アプリケーション内部から読み込むのはやめたほうが良くない?

皆さんのご意見もお待ちしています。


  1. 12 Factor App の是非についてはまた別の機会に ↩︎