Python | テスト・設計・品質:環境変数管理

Python Python
スポンサーリンク
  1. 環境変数管理って何?一言でいうと「コードに書いちゃいけない“秘密と設定”を外に出してあげること」
  2. まずは「環境変数を読む」超基本から押さえる
    1. Pythonで環境変数を読む最小コード
  3. 重要ポイント1:秘密情報を「絶対に」コードに書かない
    1. やってはいけない典型例
    2. 正しい方向性:コードは「キー名」だけ知っていればいい
  4. 重要ポイント2:環境ごとの差を「環境変数」で吸収する設計にする
    1. if 本番なら… をコードに書かない
    2. 良い方向性:環境ごとに「値だけ」変える
  5. 重要ポイント3:.envファイルとpython-dotenvの使い方
    1. ローカル開発では「.env」を使うと楽になる
  6. 重要ポイント4:型とバリデーションをちゃんとやる
    1. 環境変数は全部「文字列」で来る問題
    2. Pydanticや設定クラスでまとめるのもアリ
  7. テスト・品質と環境変数管理の関係
    1. テストでは「環境変数を差し替えられる」設計にする
    2. CI / Docker / 本番運用ときれいにつながる
  8. 初心者が環境変数管理を身につけるためのステップ
    1. ステップ1:まず「秘密をコードに書かない」を徹底する
    2. ステップ2:.env + python-dotenv でローカル開発を整える
    3. ステップ3:設定をクラスにまとめて、テストから差し替えられるようにする
  9. まとめ(環境変数管理は「秘密と環境差をコードの外に追い出す設計」)

環境変数管理って何?一言でいうと「コードに書いちゃいけない“秘密と設定”を外に出してあげること」

環境変数管理は、
「本番・開発・テストで変わる値」や「絶対にコードに埋め込んではいけない秘密」を、
コードの外に出して、安全かつ一貫したやり方で扱うための仕組みづくりです。

例えば、こんなものは全部「環境変数で管理すべきもの」です。

DBの接続URL
APIキー・トークン・パスワード
本番と開発で違うフラグ(DEBUGモードなど)

これを settings.py にベタ書きすると、
Git に乗る → GitHub に上がる → 永久にインターネットに残る、という地獄コースになります。

だからこそ、「環境変数で管理する」という設計が、
テスト・設計・品質・セキュリティの土台になります。


まずは「環境変数を読む」超基本から押さえる

Pythonで環境変数を読む最小コード

環境変数は os.environ から読みます。

import os

DATABASE_URL = os.environ["DATABASE_URL"]
DEBUG = os.environ.get("DEBUG", "false").lower() == "true"
Python

os.environ["KEY"]
必須。なければ KeyError で落ちる(=設定漏れにすぐ気づける)。

os.environ.get("KEY", "default")
あれば使う、なければデフォルトを使う。

ここで大事なのは、

設定がないのに動いてしまう」状態を作らないこと
ないなら落ちる」か「明示的なデフォルト」のどちらかにすること

です。


重要ポイント1:秘密情報を「絶対に」コードに書かない

やってはいけない典型例

例えば、こういうコードは完全にアウトです。

DATABASE_URL = "postgresql://user:password@db:5432/app"
API_KEY = "sk-xxxxxxxxxxxxxxxx"
Python

これを Git にコミットした瞬間、
その秘密は「永遠に履歴に残る」ことになります。

GitHub に上げたら、もう取り返しがつきません。

正しい方向性:コードは「キー名」だけ知っていればいい

コード側は「どの環境変数を読むか」だけ知っていればよくて、
値そのものは一切知らない状態が理想です。

import os

DATABASE_URL = os.environ["DATABASE_URL"]
API_KEY = os.environ["EXTERNAL_API_KEY"]
Python

値は、

ローカルなら .env やシェルの設定
Dockerなら -eenv_file
GitHub Actionsなら secrets

などで注入します。

「コードは鍵穴だけ持つ。鍵そのものは外から差し込む」
この感覚がめちゃくちゃ大事です。


重要ポイント2:環境ごとの差を「環境変数」で吸収する設計にする

if 本番なら… をコードに書かない

悪い例を見てみます。

import os

ENV = os.environ.get("ENV", "dev")

if ENV == "prod":
    DATABASE_URL = "postgresql://prod-user:prod-pass@prod-db:5432/app"
else:
    DATABASE_URL = "sqlite:///dev.db"
Python

これ、本番の接続情報がコードに埋まっている時点でアウトです。

さらに、「環境ごとの差」を if 文で書き始めると、
コードがどんどん環境依存で汚れていきます。

良い方向性:環境ごとに「値だけ」変える

コードはこうしておきます。

import os

DATABASE_URL = os.environ["DATABASE_URL"]
Python

開発環境では:

export DATABASE_URL=sqlite:///dev.db

本番環境では:

export DATABASE_URL=postgresql://prod-user:prod-pass@prod-db:5432/app

Dockerなら:

services:
  app:
    image: myapp
    environment:
      - DATABASE_URL=postgresql://...
YAML

GitHub Actionsなら:

env:
  DATABASE_URL: ${{ secrets.DATABASE_URL }}
YAML

コードは一切変えずに、環境変数だけ変える。
これが「環境変数管理」の本質です。


重要ポイント3:.envファイルとpython-dotenvの使い方

ローカル開発では「.env」を使うと楽になる

毎回 export するのは面倒なので、
ローカルでは .env ファイルを使うのが定番です。

.env:

DATABASE_URL=sqlite:///dev.db
DEBUG=true
EXTERNAL_API_KEY=dev-xxxxxxx

これを Python 起動時に読み込ませます。

from dotenv import load_dotenv
import os

load_dotenv()

DATABASE_URL = os.environ["DATABASE_URL"]
DEBUG = os.environ.get("DEBUG", "false").lower() == "true"
Python

ここで絶対に守るルールが一つあります。

.env は Git にコミットしない(.gitignore に入れる)

.gitignore:

.env

.env.example のような「サンプルだけ」をコミットしておきます。

.env.example:

DATABASE_URL=sqlite:///dev.db
DEBUG=true
EXTERNAL_API_KEY=your-api-key-here

これで、

「何の環境変数が必要か」は共有できる
「実際の値」は各自の .env にだけ存在する

という状態を作れます。


重要ポイント4:型とバリデーションをちゃんとやる

環境変数は全部「文字列」で来る問題

環境変数は、OSレベルでは全部「文字列」です。

DEBUG=true
PORT=8000
MAX_RETRY=3

全部文字列として渡されます。

なので、Python側で型変換とバリデーションをちゃんとやるのが大事です。

import os

def get_bool(name: str, default: bool = False) -> bool:
    raw = os.environ.get(name)
    if raw is None:
        return default
    return raw.lower() in ("1", "true", "yes", "on")

def get_int(name: str, default: int) -> int:
    raw = os.environ.get(name)
    if raw is None:
        return default
    try:
        return int(raw)
    except ValueError:
        raise ValueError(f"Invalid int for {name}: {raw!r}")

DEBUG = get_bool("DEBUG", default=False)
PORT = get_int("PORT", default=8000)
Python

ここでのポイントは、

「おかしな値が来たら、早めに落ちる」ようにすること

です。

DEBUG=maybe とか
PORT=abc とかが来たときに、
中途半端に動き続けるのが一番危険です。

Pydanticや設定クラスでまとめるのもアリ

もう少しちゃんとやるなら、
設定をクラスにまとめるのもよくあるパターンです。

from pydantic import BaseSettings

class Settings(BaseSettings):
    database_url: str
    debug: bool = False
    port: int = 8000

    class Config:
        env_file = ".env"

settings = Settings()
Python

これで、

settings.database_url
settings.debug
settings.port

のように、型付きでアクセスできます。


テスト・品質と環境変数管理の関係

テストでは「環境変数を差し替えられる」設計にする

環境変数をガチガチにグローバルで読んでしまうと、
テストが書きにくくなります。

例えば、モジュールインポート時にこうしてしまうと:

import os

DATABASE_URL = os.environ["DATABASE_URL"]
Python

テストごとに値を変えたいときに面倒です。

一つの工夫として、「設定をオブジェクトにまとめて渡す」設計があります。

from dataclasses import dataclass
import os

@dataclass
class Settings:
    database_url: str
    debug: bool

def load_settings() -> Settings:
    return Settings(
        database_url=os.environ["DATABASE_URL"],
        debug=os.environ.get("DEBUG", "false").lower() == "true",
    )
Python

アプリ側は Settings を受け取るようにします。

def run_app(settings: Settings) -> None:
    print("DB:", settings.database_url)
Python

テストでは、環境変数をいじらずに「テスト用設定」を渡せます。

def test_run_app_uses_given_settings():
    settings = Settings(database_url="sqlite:///:memory:", debug=True)
    run_app(settings)
Python

「環境変数を読む場所」と「アプリ本体」を分離することで、
テストしやすさと品質が一気に上がります。

CI / Docker / 本番運用ときれいにつながる

環境変数管理をちゃんとしておくと、

ローカル:.env
CI:GitHub Actions の env / secrets
Docker:environment / env_file
本番:サーバーの環境変数設定

というふうに、どこでも同じキー名で設定を渡せるようになります。

コードは一切変えずに、
「どこで動かすか」だけ変えられる。

これは、テスト・設計・品質・運用の全部に効いてきます。


初心者が環境変数管理を身につけるためのステップ

ステップ1:まず「秘密をコードに書かない」を徹底する

APIキー・パスワード・DB URL を、
一切コードに書かない、と決める。

代わりに、必ず os.environ["KEY"] で読む形にする。

これだけでも、設計の意識がガラッと変わります。

ステップ2:.env + python-dotenv でローカル開発を整える

.env を作り、load_dotenv() で読み込む形にして、
ローカルでの開発を快適にする。

.env は Git にコミットしない。
代わりに .env.example を置いて、「必要なキー一覧」を共有する。

ステップ3:設定をクラスにまとめて、テストから差し替えられるようにする

Settings クラスを作り、
環境変数読み込みは load_settings() に閉じ込める。

アプリ本体は Settings を受け取るようにして、
テストでは好きな値を渡せるようにする。

ここまでできると、
環境変数管理が「ただのテクニック」ではなく、
設計と品質の一部として体に馴染んできます。


まとめ(環境変数管理は「秘密と環境差をコードの外に追い出す設計」)

初心者目線で整理すると、環境変数管理はこういうものです。

秘密情報や環境ごとに変わる設定を、コードにベタ書きせず、環境変数として外から注入することで、「コードは鍵穴だけ持ち、鍵そのものは持たない」状態を作る設計。
.env・python-dotenv・環境変数の型変換・設定クラスなどを組み合わせると、ローカル・CI・Docker・本番のどこでも同じキー名で設定を渡せるようになり、テストしやすく、セキュアで、環境差に強いアプリになる。
最初は「秘密をコードに書かない」「.env を使う」「設定をクラスにまとめる」という3つだけ意識して、小さなプロジェクトから実際に手を動かしてみると、環境変数管理の感覚が一気に掴めてくる。

タイトルとURLをコピーしました