Pythonやアプリケーション開発をしていると、設定値の置き場所として「環境変数」「.env ファイル」「コード内のデフォルト値」の3つがよく登場する。この3つは互いに競合することがあり、「なぜこの値が使われているのか分からない」というハマりどころになりやすい。今回は、この3つがどういう優先順位で解決されるのか、そしてなぜその順番になっているのかを整理する。
そもそも .env ファイルとは何か
補足:
.envとは、KEY=valueの形式で設定値を1行ずつ書いたテキストファイルのこと。APIキーやDB接続文字列のような「環境ごとに変わる値」「リポジトリに直接書きたくない値」を、コードから切り離して管理するために使う。多くの場合.gitignoreに含めてバージョン管理対象から外す。
Pythonでは python-dotenv というライブラリが定番で、load_dotenv() を呼ぶと .env ファイルの中身が読み込まれ、os.environ に反映される。呼び出し側のコードは os.environ.get('API_KEY') のように、.env 経由か本物のOS環境変数かを意識せずに同じ書き方で値を取得できる。
3つの置き場所と、その優先順位
設定値は一般的に、次の3段階のどこかに存在しうる。
- OS環境変数(シェルで
export API_KEY=...した値、あるいはCIやDockerコンテナが起動時に注入する値) .envファイル(リポジトリ直下に置かれ、開発者のローカル環境でだけ読み込まれる値)- コード内のデフォルト値(
os.environ.get('API_KEY', 'default-value')のように、どこにも設定がない場合の最終フォールバック)
多くのdotenv系ライブラリ(python-dotenv を含む)は、この3つに対して「すでにOS環境変数として設定されている値は、.env ファイルの値で上書きしない」という挙動をデフォルトにしている。python-dotenv の load_dotenv() は、override=True を明示しない限り、既存の os.environ にある値を尊重し、.env 側の同名キーは無視する。
つまり優先順位は「OS環境変数 > .env ファイル > コード内デフォルト値」の順になる。
なぜこの順番が理にかなっているのか
一見すると「あとから読み込んだ .env の値が勝つ方が直感的では」と思うかもしれないが、実際にはOS環境変数を優先させる設計には理由がある。
典型的なケースは、CI(継続的インテグレーション)環境やDockerコンテナでの実行だ。これらの環境では、シークレット管理の仕組み(GitHub Actionsのsecrets、Dockerの -e オプション等)を通じて、本番用・CI用の値がOS環境変数として直接注入されることが多い。このとき、もしリポジトリに同梱された開発用の .env ファイル(ダミー値や開発者のローカル値が入っている)が、意図せずCIから注入された本番相当の値を上書きしてしまったら、テストや本番相当の動作確認が正しい値で行われなくなってしまう。
「すでに明示的に設定されている値は、後から読み込むファイルで勝手に上書きしない」という原則は、設定の優先順位設計における一般的な考え方でもある。「その場その場でより意図的に設定された値ほど優先される」という順序にしておけば、開発者のローカル環境では .env の値が(OS環境変数が未設定なら)そのまま使われ、CI・本番環境ではインフラ側が注入した値が .env の中身に関係なく確実に使われる、という両立が実現できる。
自社実装での類似パターン — 明示設定 > 自動検出 > ハードコードのフォールバック
このアプリでは .env そのものは使っていないが、core/log_i18n.py のUI言語自動判定(get_ui_language())に、構造としてよく似た多段フォールバックがある。
# 判定順序(最初に有効な値で確定):
# 1. 環境変数 LC_ALL / LC_MESSAGES / LANG(POSIX 標準)
# 2. Windows API: GetUserDefaultUILanguage
# 3. locale.getlocale() / locale.getdefaultlocale()(後方互換)
# 4. すべて取得失敗 → 'en'(海外配布優先)
さらにこの関数を呼び出す側では、settings.json に明示的な言語指定があれば、この自動判定そのものをスキップして最優先で使う設計になっている。
この構造を優先順位として並べると、「ユーザーが明示的に選んだ設定(settings.json)> OSやシェルが持っている環境情報(LANG 等)> Pythonの標準ライブラリによる推定(locale モジュール)> どれも取れない場合の固定フォールバック('en')」という4段階になる。これは .env の優先順位設計と同じ発想の応用だ。「意図的に設定された値」が「自動的に検出・推測された値」より常に優先され、さらにその下に「何もなくても必ず動く」ための最終フォールバックを置く。設定値の解決順序を設計するときの一般原則として、覚えておいて損はない。
まとめ
環境変数・.env ファイル・コード内デフォルト値の3つは、「OS環境変数 > .env > コード内デフォルト値」という順で解決されるのが一般的な挙動であり、これはCIやコンテナ環境から注入された値を、開発用の .env ファイルが誤って上書きしないようにするための設計だ。この「より明示的・意図的に設定された値を優先し、下位には常に安全なフォールバックを置く」という考え方は、.env に限らず、アプリケーションの設定値解決全般に応用できる汎用的な原則と言える。