Key idea
Build your app once, then run that same build in development, staging and production, giving it different configuration in each. What you tested in staging is then exactly what reaches production.
Build, then run
Getting code live happens in two steps:
- Build: turn the code into something runnable. That means installing dependencies, compiling, and bundling front-end files. The result is often a container image, which Path 3 covers.
- Run: start the build with this environment's configuration.
Configuration belongs in step 2. Then one build works anywhere: staging gives it the staging database, production gives it the production one.
Why it matters
What you tested is what you ship. If production gets its own build, it runs something nobody tested. A dependency may have moved on, or a build step may have behaved differently that day.
Moving a release is fast. Promoting to production means starting the same build with production values, not waiting for a new one. Rolling back means starting the previous build again.
Secrets stay out of the build. A build can copy your .env into the finished app, and a key that gets in stays there. Anyone who can download the build can find it, even after you change the key in your settings. Keep .env out of the build; Path 3 shows how in lesson 3.4.4.
When configuration gets baked in
Some values are read while building, not while running. The value in effect at that moment is copied into the output, and changing it later does nothing until you build again.
The most common case is front-end code. A browser can't read your server's environment variables, so frameworks copy chosen ones into the JavaScript files at build time. In Next.js these are variables named NEXT_PUBLIC_…; in Vite, VITE_….
// Built with NEXT_PUBLIC_API_URL=https://staging-api.example.com
fetch(`${process.env.NEXT_PUBLIC_API_URL}/orders`);
// The built file contains the staging URL itself. Production would call staging.
That breaks "build once" for these values, and it's a real trade-off, not a mistake. You have two honest options:
- Build once per environment for the front end, with each environment's values. Simple, but each build is only tested where it's built.
- Load the settings at run time. The server hands the browser a small settings file, or the front end calls a relative path like
/apiso the address never changes.
Either way, never put a secret in one of these variables. Everything in them ends up in files every visitor downloads.
How to spot a baked-in value
- A variable change had no effect until a rebuild.
- Searching the built files (
.next/,dist/) finds the value itself. - The build step is given a value that differs by environment.
Any of these means the value is read at build time. Either move it to run time or build per environment on purpose.
Check yourself