In short
Your app runs in at least three places: your laptop, staging and production. Same code, different database, keys and log levels. Mixing them up is how test emails reach real customers.
Managing different configuration (database URLs, API keys, feature flags) across local, staging and production environments without hardcoding any of it.

Think of it like this
One recipe, three kitchens
A caterer uses the same biryani recipe at home, at a tasting and at a 500-guest wedding. Only the pot size and spice quantities change. Your code is the recipe. Config is the quantities, kept separately for each kitchen.
Watch it happen
Where a config value comes from
1/5Code ships with safe defaults, like PORT=3000 and LOG_LEVEL=info.
The big ideas
What you need to know
Same build, different config
Change behaviour by changing values, not code. The artifact you tested on staging is the one that runs in production.
Secrets never go in Git
Commit a .env.example with dummy values. The real .env sits in .gitignore.
Validate at startup
Check every variable when the app boots and fail loudly. A missing value should stop the deploy, not a user's checkout.
NODE_ENV is not enough
NODE_ENV=production tweaks library behaviour. Use your own APP_ENV, like staging or production, for your own logic.
See it in code
Walk through the code
1// config.ts2import { z } from "zod";3 4const schema = z.object({5 APP_ENV: z.enum(["development", "staging", "production"]),6 PORT: z.coerce.number().default(3000),7 DATABASE_URL: z.string().url(),8 LOG_LEVEL: z.enum(["debug", "info", "warn", "error"]).default("info"),9});10 11const parsed = schema.safeParse(process.env);12if (!parsed.success) {13 console.error("Bad config:", parsed.error.issues);14 process.exit(1);15}16export const config = parsed.data;1/4Describe every variable once: allowed values, type and default.
Every other file imports config from here. If a variable is missing or wrong, the app refuses to start and tells you exactly which one.
Save yourself the pain
Common mistakes
Oops
Committing .env 'just for now' to share it with a teammate.
Do this instead
Once it's in Git history it's leaked, even after deleting. Rotate the keys and share secrets through a password manager.
Oops
Writing if (process.env.NODE_ENV === 'production') all over the code.
Do this instead
Read one config object with clear flags like config.sendRealEmails. Easier to test and to change.
Your mission
Lock down your config
Quick check
Staging and production run the same Docker image. How should each end up using a different database?
Go deeper
- 1Set up separate `.env` files (or equivalents) for local, staging and production.
- 2Learn to never commit real production secrets to any config file in git.
- 3Practice validating required environment variables at app startup, failing fast if any are missing.
More topics at this stop
Stuck on this topic? Ask an Insider
Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.