環境変数管理って何?一言でいうと「コードに書いちゃいけない“秘密と設定”を外に出してあげること」
環境変数管理は、
「本番・開発・テストで変わる値」や「絶対にコードに埋め込んではいけない秘密」を、
コードの外に出して、安全かつ一貫したやり方で扱うための仕組みづくりです。
例えば、こんなものは全部「環境変数で管理すべきもの」です。
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"
Pythonos.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なら -e や env_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://...
YAMLGitHub 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_urlsettings.debugsettings.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つだけ意識して、小さなプロジェクトから実際に手を動かしてみると、環境変数管理の感覚が一気に掴めてくる。
