Env & Deployment
Ensure that applications are easy to configure, safe to deploy, and resilient across environments (dev, staging, production).
Proper environment and deployment practices reduce downtime, prevent misconfiguration, and support predictable releases.
Use environment variables for configuration
Store environment-specific values like API keys, database URLs, and feature flags in environment variables — never hard-code them in the codebase. Use .env
files for local dev, and secrets managers or platform config for production.
const dbUrl = "postgresql://localhost:5432/dev-db";
const dbUrl = process.env.DATABASE_URL;
Validate environment variables at startup
Use schema validation (e.g. with zod
, joi
, or custom checks) to ensure all required environment variables are defined and correctly formatted when the app starts. Fail fast if critical variables are missing.
1if (!process.env.API_KEY) {
2 throw new Error("Missing required API_KEY");
3}
Separate configuration from code
Keep your configuration (env vars, JSON/YAML files, secrets) outside of your application logic. This makes the app portable, testable, and environment-agnostic.
Use config loading modules (
config
,dotenv
, etc.)Avoid environment-specific conditionals in logic
Pass config as parameters to services and classes
Ensure build-time configuration is immutable
Values needed at build time (e.g. NEXT_PUBLIC_*
variables in Next.js) should not change at runtime. Don’t rely on mutable config in frontend builds — the app won’t see changes until it's rebuilt and redeployed.
Expose public variables via
VITE_
,NEXT_PUBLIC_
, etc.Clearly separate runtime and build-time config
Use CI/CD to inject correct variables before build
Avoid committing secrets to source control
Never commit API keys, tokens, or credentials to Git. Use .env
files excluded via .gitignore
, and rotate any exposed secrets immediately. Automate detection of secrets in CI if possible.
Tools:
Use feature toggles for safe rollouts
Use feature toggles (flags) to enable or disable functionality at runtime without redeploying. This helps with safe rollouts, A/B testing, and controlled beta releases.
Store toggles in remote config or database
Group by feature, not by user
Clean up old toggles regularly
Automate deployment via CI/CD
Automate builds, tests, and deployments with continuous integration and delivery pipelines. This reduces human error, increases repeatability, and ensures consistent environments across stages.
Use tools like GitHub Actions, GitLab CI, CircleCI
Define separate stages: build → test → deploy
Run linters, type checks, and unit tests before deploy
Implement health and readiness checks
Add /healthz
or /ready
endpoints to expose application health for monitoring and orchestration. These are essential for containers (Kubernetes, Docker) and load balancers to determine if your app is healthy and ready to serve traffic.
/healthz
→ reports basic status/ready
→ confirms readiness (DB, services, etc.)
Make rollbacks easy and safe
Design your deployment process to support quick rollback in case of failure. Avoid migrations or state changes that are hard to revert. Store previous builds or use blue-green or canary deployments to switch back easily.
Keep database schema backward-compatible during rollout
Tag previous releases for easy re-deploy
Monitor metrics and error rates during deployment