Design preview, staging and production environments and promote one tested artifact through them.
Why it matters
Testing a change on a developer laptop is not the same as running it next to real infrastructure, real configuration and real traffic. Teams therefore run several copies of their system, called environments, and move each change through them before users see it. Getting this design right is a core part of a cloud engineer's job: too few environments and bugs reach users; too many and releases slow to a crawl while the cloud bill grows.
The common environments
| Environment | Purpose | Who uses it | Data |
|---|---|---|---|
| Local / dev | Write and debug code | One developer | Fake or seeded |
| Preview | One short-lived copy per pull request, so reviewers can click through a change | Reviewers, designers | Seeded test data |
| Staging | A production-like copy for final checks, integration and load tests | The team, QA | Test or anonymized data |
| Production | The real system | Users | Real data |
Preview environments are created automatically when a pull request opens and deleted when it closes. Many platforms (Vercel, Netlify, Render, and Kubernetes setups with tools such as Argo CD) do this out of the box. Their short lifetime keeps cost down; forgetting to delete them is a classic source of surprise bills.
Staging is only useful if it resembles production: same container images, same infrastructure code, same kinds of services, smaller in size. This is called environment parity. A staging environment that runs a different database version or skips the load balancer will miss exactly the bugs it exists to catch.
Build once, promote the artifact
The most important rule: build an artifact once and promote that same artifact from environment to environment. If you rebuild for each environment, the image that reaches production is not the one you tested. Dependencies may have changed, a base image may have been updated, or a build flag may differ.
commit 3f9c2a1
|
v
CI builds image hello-devops:3f9c2a1 (once)
|
+--> preview runs hello-devops:3f9c2a1
+--> staging runs hello-devops:3f9c2a1 -> integration tests pass
+--> production runs hello-devops:3f9c2a1 -> after approval
What differs between environments is configuration, not code: database URLs, feature flag settings, log levels, replica counts. Provide it at deploy time through environment variables and a secret store, following the twelve-factor app principle of keeping config in the environment.
Promotion means moving that artifact to the next environment. It can be:
- Automatic: every green build on
maindeploys to staging. - Gated: production requires a manual approval, a change window or passing checks. GitHub Actions supports this with environments that have protection rules such as required reviewers, and each environment can have its own secrets.
- Branch-based: some teams fast-forward a
productionbranch to a tested commit and let the platform deploy that branch. The principle is the same: production gets a commit that already passed everywhere else.
Deployment strategies
How you replace the old version matters as much as where:
- Recreate: stop the old version, start the new one. Simple, but users see downtime.
- Rolling update: replace instances a few at a time (the Kubernetes default). No downtime, but old and new versions run side by side for a while.
- Blue-green: run the new version (green) next to the current one (blue) at full size, test it, then switch all traffic at the load balancer. Rollback is a switch back. It needs double capacity during the release.
- Canary: send a small share of traffic (say 5%) to the new version, watch its metrics, then increase. The next lesson plans one.
Databases are the hard part
Code rolls back easily; data does not. If version 2 renames a column and you roll back to version 1, version 1 breaks. The safe pattern is expand and contract: first add the new column while keeping the old one (both versions work), migrate the data, switch the code, and only remove the old column in a later release. Every migration should be backward compatible with the version currently running.
Common mistakes
- Rebuilding the artifact for production.
- Staging that has drifted from production, so it no longer predicts anything.
- Sharing one database between staging and production, or copying real customer data into test environments without anonymizing it.
- Preview environments that never get deleted.
- Storing production secrets where staging jobs can read them. Scope secrets per environment.
Quick recap
- Preview, staging and production each have a job; staging must resemble production.
- Build once, then promote the same immutable artifact; only configuration changes.
- Gate production with approvals or checks.
- Choose a strategy (rolling, blue-green, canary) and keep database changes backward compatible.
Try it
This lesson has no hands-on activity. Use the quiz in the Do tab to test your environment design.