Table of Contents
A comprehensive guide to managing environment variables in Python applications. It compares seven approaches: direct `os.environ` manipulation, `.env` files with `python-dotenv`, shell exports, inline invocation, secrets managers, VS Code settings, and `direnv`. Each method is evaluated on security, ease of use, scalability, and migration difficulty. **Winner for "vibe coding" (rapid prototyping):** `.env` + `python-dotenv` offers the best balance with excellent management convenience, adequate security for development, good scalability, and seamless migration paths. The post recommends centralizing environment variable reads in a `config.py` file and, for Jupyter notebooks, combining VS Code settings with `load_dotenv()` for reliability. This approach allows frictionless upgrades to production-grade secrets managers when needed.
What Are Environment Variables?
Environment variables are key-value pairs stored outside your source code that configure how your application behaves. Instead of hardcoding sensitive information — like API keys, database passwords, or feature flags — directly into your scripts, you store them in the environment and read them at runtime. This keeps secrets out of version control, makes your app easier to configure across different machines and deployment stages, and follows the widely accepted 12-factor app principle of separating config from code.
In Python, you access them via os.environ or os.getenv("MY_VAR"). The question isn’t really whether to use environment variables — it’s how to manage them effectively. That’s exactly what this guide covers.
All Approaches with Pros & Cons
Approach 1: os.environ directly in code
import os
os.environ['MY_VAR'] = 'some_value'
Advantages: – Simple and straightforward, no extra tools needed – Dynamic — can set variables conditionally at runtime – No external dependencies required
Disadvantages: – Only affects the current process and its children, not the parent shell – Hardcoding values in source code is a security risk (especially for secrets) – Not reusable across different scripts or environments – Poor separation of configuration from code
Approach 2: .env file with python-dotenv
from dotenv import load_dotenv
load_dotenv() # loads from .env file
Advantages: – Clean separation of config from code
– Easy to manage per-environment configs (dev, staging, prod)
– Widely adopted convention; works well with version control (just .gitignore the file)
– Simple to use and understand
– Works consistently across Windows, macOS, and Linux
Disadvantages: – Requires an external dependency (python-dotenv)
– .env files can accidentally be committed to version control
– Not suitable for production secrets without additional care
– Plaintext storage on disk
Approach 3: Shell environment variables
export MY_VAR=value # Linux/macOS
set MY_VAR=value # Windows CMD
$env:MY_VAR="value" # PowerShell
python script.py
Advantages: – No code changes needed; purely external – Variables are available to the process immediately – Good for CI/CD pipelines and containerized environments – No external dependencies
Disadvantages: – Manual and error-prone for complex setups – Not persistent across sessions unless added to shell profile – Harder to reproduce consistently across machines – Platform-specific syntax
Approach 4: Inline invocation
MY_VAR=value python script.py # Linux/macOS only
Advantages: – Scoped only to that single command, so no shell pollution – Quick and convenient for one-off overrides – Clean and isolated
Disadvantages: – Linux/macOS only (not natively supported on Windows) – Not practical for many variables – Not reusable or self-documenting
Approach 5: Secrets Manager (AWS Secrets Manager, HashiCorp Vault, Azure Key Vault)
import boto3
secret = boto3.client('secretsmanager').get_secret_value(SecretId='my_secret')
os.environ['MY_VAR'] = secret['SecretString']
Advantages: – Most secure approach for production systems – Centralized management, auditing, and rotation of secrets – Works well across teams and infrastructure – Enterprise-grade security – Compliance-ready
Disadvantages: – Significant setup overhead and added complexity – Requires cloud/infra dependencies – Overkill for small or local projects – Additional costs involved
Approach 6: VS Code/Cursor settings.json (for Jupyter)
// .vscode/settings.json
{
"python.envFile": "${workspaceFolder}/.env"
}
Advantages: – Automatic loading — no code needed in notebooks – Variables injected before the kernel starts – Zero notebook boilerplate – IDE-integrated solution
Disadvantages: – VS Code/Cursor-specific feature – Reduces portability (won’t work in JupyterLab or other environments) – IDE-dependent
Approach 7: direnv (macOS/Linux)
# install once
brew install direnv # macOS
# in your project root
echo 'dotenv' > .envrc
direnv allow
Advantages: – Auto-loads .env when you cd into a directory
– Zero notebook/script boilerplate
– Kernel/process inherits vars automatically
– Shell-level integration
Disadvantages: – macOS/Linux only – Requires shell integration setup – Additional tool to learn and maintain – Not available on Windows
Quick Comparison Table
| Approach | Security | Ease of Use | Management | Scalability | Migration | Best For |
|---|---|---|---|---|---|---|
os.environ in code | ❌ Low | ✅ Easy | ❌ Poor | ❌ Poor | ⚠️ Medium | Quick prototyping |
.env + dotenv | ⚠️ Medium | ✅ Easy | ✅ Good | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | Local dev, vibe coding |
| Shell export | ⚠️ Medium | ✅ Easy | ⚠️ Medium | ⭐⭐⭐ | ⭐⭐⭐⭐ | CI/CD, scripting |
| Inline invocation | ⚠️ Medium | ✅ Easy | ❌ Poor | ❌ Poor | ⭐⭐⭐ | One-off overrides |
| Secrets manager | ✅ High | ❌ Complex | ✅ Excellent | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | Production systems |
| VS Code settings.json | ⚠️ Medium | ✅ Easy | ✅ Good | ⚠️ Medium | ⚠️ Medium | Jupyter in VS Code |
direnv | ⚠️ Medium | ⭐⭐⭐⭐ | ✅ Good | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | Mac/Linux power users |
Detailed Ratings Table
| Criterion | os.environ | .env + dotenv | Shell export | Inline | Secrets Manager | VS Code settings | direnv |
|---|---|---|---|---|---|---|---|
| Management Convenience | ⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| Security | ⭐ | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ |
| Scalability | ⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ |
| Migration Ease | ⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ |
| Cross-platform | ✅ | ✅ | ⚠️ | ❌ | ✅ | ✅ | ❌ |
| No Dependencies | ✅ | ❌ | ✅ | ✅ | ❌ | ✅ | ❌ |
3. Best Approach for Vibe Coding
🏆 Winner: .env file + python-dotenv
For vibe coding (rapid prototyping / solo or small-team development), .env + python-dotenv is the clear winner. Here’s the detailed breakdown:
Management Convenience: ⭐⭐⭐⭐⭐
- One file to rule all config — easy to scan, edit, and share with teammates (minus secrets)
- IDE support — VS Code, PyCharm, Cursor, and most modern tools have native
.envsupport - Environment switching is as simple as swapping
.envfiles (.env.dev,.env.prod) - Cross-platform consistency — works on Windows, macOS, and Linux with no shell-specific syntax
- Documentation — pair with
.env.example(committed to git) to document required vars without exposing values
Security: ⭐⭐⭐ (Sufficient for early stage)
- Secrets stay out of source code — the most critical baseline
- Simple
.gitignoreentry keeps the file out of version control - ⚠️ Caveat: Plaintext on disk, so not suitable for production secrets without upgrading to a secrets manager later
- Good enough for development and early-stage projects
- Clear upgrade path to more secure solutions as you scale
Scalability: ⭐⭐⭐⭐
- Works well up to mid-size teams and projects
- Layerable — can add tools like
direnvor Docker Compose’senv_filewithout changing code - Stable interface —
os.getenv()calls remain unchanged when you graduate to a secrets manager - Multiple environments — easy to manage dev, staging, prod configurations
Migration Difficulty: ⭐⭐⭐⭐⭐ (Very Easy)
This is where .env + dotenv truly shines. The migration path is nearly frictionless:
| Stage | What you change | Code changes needed |
|---|---|---|
| Local dev → Staging | Replace .env with CI/CD injected env vars | None |
| Staging → Production | Point to AWS Secrets Manager, Vault, etc. | Minimal (one file) |
| Monolith → Microservices | Each service gets its own .env | None (same pattern) |
Since python-dotenv simply populates os.environ, and all good Python code reads from os.environ / os.getenv(), the rest of your codebase is completely decoupled from how vars are injected.
The Ideal Vibe Coding Setup
Project Structure
project/
├── .env ← your actual secrets (gitignored)
├── .env.example ← committed template, no real values
├── .gitignore ← includes .env
└── config.py ← single place that reads all env vars
config.py — Centralize all env reads
import os
from dotenv import load_dotenv
load_dotenv()
DATABASE_URL = os.getenv("DATABASE_URL", "sqlite:///dev.db")
API_KEY = os.getenv("API_KEY")
DEBUG = os.getenv("DEBUG", "false").lower() == "true"
Why this works: – When you migrate to a secrets manager, you only update one file – No scattered os.getenv() calls throughout your codebase
– Clear, maintainable configuration management
Special Case: Jupyter Notebooks in VS Code/Cursor
For Jupyter interactive kernels, layer two approaches for maximum reliability:
Step 1: Add to .vscode/settings.json
{
"python.envFile": "${workspaceFolder}/.env"
}
Step 2: Call load_dotenv() as a safety net
from dotenv import load_dotenv
load_dotenv(override=True)
Why both? – VS Code/Cursor auto-injects vars before kernel starts (zero boilerplate)
– load_dotenv() ensures it works even if IDE injection fails
– Covered whether working in VS Code, Cursor, or JupyterLab
Summary & Recommendation
For vibe coding applications:
✅ Primary approach: .env + python-dotenv – ⭐⭐⭐⭐⭐ Management Convenience
– ⭐⭐⭐ Security (sufficient for dev)
– ⭐⭐⭐⭐ Scalability
– ⭐⭐⭐⭐⭐ Migration Ease
✅ For Jupyter: Add python.envFile in VS Code settings + load_dotenv() safety net
✅ Migration path: 1. Start with .env + dotenv
2. Centralize reads in config.py 3. Upgrade to secrets manager when ready (change one file)
The .env approach isn’t the most secure in absolute terms, but it hits the right balance for vibe coding — low friction to start, low friction to grow.






The workflow details are helpful. They fit well with small browser utilities like Fileformatconverter for quick conversion and everyday technical tasks.
I appreciated the clear framing here. It connects well with practical web resources such as Prompt Optimizer.
This was a useful read, especially the practical framing. It fits naturally beside related web resources like Numberblocks Generator Websim.
I like the focus on making small technical tasks simpler. A browser-based utility such as Persona 5 Royal Fusion Calculator makes sense when installing software would be too much overhead.
This was a useful read, especially the practical framing. It fits naturally beside related web resources like Translation Tool.
This was a useful read, especially the practical framing. It fits naturally beside related web resources like Verity Mod.
The concrete examples make this easier to apply. Readers comparing related resources may also find Download Enumeration Form Karnataka useful in this context.
I appreciated the clear framing here. It connects well with practical web resources such as Prompt Optimizer.
I found the creative workflow angle useful. Resources such as Anime Vanguards Tier List are handy when testing visual ideas before moving into heavier editing tools.
I found the creative workflow angle useful. Resources such as Anime Vanguards Tier List are handy when testing visual ideas before moving into heavier editing tools.
This was a useful read, especially the practical framing. It fits naturally beside related web resources like Numberblocks Generator Websim.
This was a useful read, especially the practical framing. It fits naturally beside related web resources like Funtrans.
This was a useful read, especially the practical framing. It fits naturally beside related web resources like Funtrans.
This was a useful read, especially the practical framing. It fits naturally beside related web resources like Tomodachipersonality.
I appreciated the clear framing here. It connects well with practical web resources such as Wordless.
The concrete examples make this easier to apply. Readers comparing related resources may also find Madden27ratings useful in this context.
This was a useful read, especially the practical framing. It fits naturally beside related web resources like Gothic Remake Truhe Silas Keller Unter Wasser.
The concrete examples make this easier to apply. Readers comparing related resources may also find Leagueclassic useful in this context.
References: Lollybet Aktionscode