What Are Environment Variables and Secrets?
Keeping configuration and credentials out of source code, and out of version control.
2 min read
A backend needs configuration that changes between environments — a database URL, an API key for a payment provider, the port it listens on — and some of that configuration is sensitive enough that leaking it would compromise the whole system. Environment variables are the standard mechanism for supplying both, without hardcoding either into the source code.
What environment variables are
An environment variable is a named value provided to a program by its surrounding process environment, rather than written into the code itself. A backend process reads them at startup:
DATABASE_URL=postgres://user:pass@db.internal:5432/app
STRIPE_SECRET_KEY=sk_live_51H...
PORT=3000
NODE_ENV=production
Most frameworks read these directly from the OS environment, or from a local .env file during development that a library loads into the process environment automatically.
Why not just hardcode config values
// Don't do this
const dbUrl = "postgres://user:hunter2@db.internal:5432/app";
Hardcoded config forces a code change (and a redeploy) every time a value needs to change between environments — local, staging, production each need a different database URL, and none of them should be the literal password sitting in a file that every contributor and every commit in the repo's history can see.
Secrets are the sensitive subset
Not all config is sensitive — a port number is fine to hardcode or commit. Secrets — database passwords, API keys, signing keys, OAuth client secrets — are the subset that grants real access if leaked, and they need extra care:
- Never commit them to version control, even in a private repository — repos get forked, cloned, mirrored, and history persists even after a later commit removes the value.
- Add
.envto.gitignorefrom the start of a project, not after a secret is accidentally committed. - Use a secrets manager in production (AWS Secrets Manager, HashiCorp Vault, or a platform's built-in encrypted environment variables) rather than plain
.envfiles on a server. - Rotate secrets that do leak immediately — treat a committed API key as compromised the instant it's pushed, even if the commit is later removed, and revoke/reissue it rather than assuming no one saw it.
A committed secret is compromised, full stop
If a secret is ever pushed to a remote repository — even briefly, even in a private repo — the correct response is to rotate it (generate a new one and revoke the old one), not just delete the commit. Git history, CI logs, and any clone made in that window can retain the old value indefinitely, so removing the commit doesn't undo the exposure.
Environment-specific behavior
Beyond secrets, environment variables commonly drive behavior differences between environments — NODE_ENV=production might disable verbose debug logging and enable stricter error handling, while NODE_ENV=development does the opposite. This is also the standard mechanism CI/CD pipelines use to inject deployment-specific values without touching code.
With authentication, middleware, rate limiting, and secrets covered, the next section turns to where most of a backend's state actually lives: the database.