Feature-Branch Environments: Platform Engineering That Developers Actually Use
How we replaced 'works on my machine' with ephemeral per-branch environments — automated creation, dependency wiring, database lifecycle and teardown.
- #platform-engineering
- #kubernetes
- #ci-cd
- #developer-experience
The most common platform engineering failure isn't technical — it's building a platform nobody asked for. The strongest signal we ever got from developers was much simpler: "I can't test my branch without stepping on someone."
So that's what we built: push a branch, get an environment.
The contract
Every feature branch gets, automatically:
- Its own namespace with the components it changed, plus shared stable versions of everything else
- A database initialized with schema + seed data, owned by the branch
- A predictable URL:
https://<branch>.dev.internal - Guaranteed teardown when the branch merges or goes stale
$ git push origin feat/new-scoring-api
# ~4 minutes later
✓ namespace feat-new-scoring-api created
✓ scoring-api → branch build
✓ ingest, web → stable
✓ postgres → initialized (schema v42 + seed)
→ https://feat-new-scoring-api.dev.internalWhat made it hard (and worth it)
Dependency wiring. The naive version deploys everything per branch — which melts your cluster and your cloud bill. The real work was a manifest per component declaring its dependencies, so a branch environment deploys only what changed and points at shared stable services for the rest.
Database lifecycle. Ephemeral environments are useless if the data story is manual. Creation runs migrations to the branch's schema version and loads seed data; teardown drops the database and reports what it cleaned. The pause/resume pattern we built for production migrations got reused here — a failed migration pauses the environment instead of leaving it half-alive.
Teardown discipline. Orphaned environments are the entropy death of this pattern. Merge events and a staleness TTL both trigger deletion, and a daily report lists every environment, its owner and its age. Nothing survives by default; survival is opt-in.
The adoption curve
We didn't mandate anything. The first team used it because their local setup took 40 minutes to boot. Within a quarter, "spin up a branch env" was just how QA validated tickets, how PMs previewed features, and how we reproduced customer-shaped bugs without touching staging.
That's the platform engineering lesson worth repeating: replace a real daily pain, make the golden path faster than the workaround, and adoption is free. Build the abstraction first and you'll spend your life evangelizing it.
The infrastructure itself — Helm charts, a small operator, CI triggers — is almost boring. The product thinking is the platform.