How do you set up CI/CD for multi-env apps
A pipeline: on push → lint/test/build; on merge → deploy. Multiple environments (dev/staging/prod) each with its own config via env vars (not baked secrets). Branch/tag-based promotion, build-once-deploy-many, preview deploys per PR, and prod gated by approvals/checks.
CI/CD for a multi-environment frontend is a pipeline plus a disciplined environment/config strategy.
The pipeline stages
CI (on every push / PR):
- Install deps (cached, lockfile-exact —
npm ci). - Lint + type-check.
- Run tests — unit, component, maybe E2E on critical paths.
- Build — produce the production artifact.
- Block the merge if anything fails.
CD (on merge / tag):
- Deploy the built artifact to the target environment.
- Run smoke tests / health checks post-deploy.
- Notify the team.
The environments
Typically dev → staging → prod (sometimes preview/QA too). Each is a real, isolated environment with its own backend URLs, API keys, feature flags, analytics IDs.
Config strategy — the multi-env core
- Environment variables, not hardcoded values. The app reads
API_URL, etc., from env. Each environment supplies its own. - No secrets in the repo or the bundle. Anything in a frontend bundle is public — only public config goes in env vars the build inlines; real secrets stay server-side. Secrets for the pipeline live in the CI provider's secret store.
- Build-once or build-per-env? Two schools: (a) build once, deploy many — one artifact, runtime config — cleaner promotion guarantees; (b) build per environment — env vars inlined at build time (common with Vite/CRA where vars are baked in). Many frontends are forced into (b); know the trade-off (you can't bit-for-bit promote a (b) artifact).
Promotion model
- Branch-based —
develop→ staging,main→ prod; or - Tag/release-based — a git tag triggers a prod deploy. Promote the same commit up the chain.
- PR preview deploys — every PR gets its own throwaway URL (Vercel/Netlify/Amplify do this automatically) so reviewers see the change live.
Production safety
- Gates — required reviews, required green checks, sometimes a manual approval step before prod.
- Gradual rollout — canary / percentage deploys, feature flags for risky changes.
- Rollback — fast, one-click revert to the previous build (atomic deploys make this trivial).
- Cache busting — content-hashed filenames so users get the new bundle; correct cache headers.
- Source maps — uploaded to error tracking (Sentry), not served publicly.
The framing
"A pipeline: every push runs lint, type-check, tests, and build; merges trigger a deploy. The multi-env part is config discipline — each of dev/staging/prod gets its config via environment variables, never hardcoded, with no real secrets in the repo or bundle since the bundle is public. I'd promote the same commit up the chain — branch- or tag-based — with PR preview deploys for review, and gate prod with required checks, approvals, gradual rollout, and an instant rollback path. The one nuance is build-once-deploy-many vs build-per-env, since most frontend tooling bakes env vars at build time."
Follow-up questions
- •Why can't you put secrets in a frontend bundle?
- •Build-once-deploy-many vs build-per-environment — trade-offs?
- •What are PR preview deploys and why are they useful?
- •How do you make production deploys safe and reversible?
Common mistakes
- •Hardcoding environment URLs/keys instead of using env vars.
- •Putting real secrets in the repo or the client bundle.
- •No staging environment — testing in prod.
- •No rollback path.
- •Not cache-busting, so users get stale bundles.
Performance considerations
- •Cache dependencies and build artifacts to keep the pipeline fast; parallelize lint/test/build. Content-hashed filenames + long cache headers optimize delivery while staying correct.
Edge cases
- •An env var missing in one environment.
- •A build that works in dev but fails in the prod build (minification, env differences).
- •A deploy that needs a coordinated backend change.
- •Rolling back a frontend while the backend already moved forward.
Real-world examples
- •GitHub Actions running lint/test/build, Vercel/Netlify auto-deploying previews per PR and prod on main.
- •Tag-based releases promoting a vetted commit from staging to production.