Client work
From five-day environment tickets to twenty minutes
A paved road for the common case, with the guardrails built in.
- Product teams
- 12
- Standard environments
- 120 requests a month, 5 business days → 20 minutes
- Releases
- 20 a week
- Manual work per release
- 45 → 12 minutes (47.63 hours a month)
- Net capacity
- $5,253.75 a month
- Rollback decision budget
- 10 minutes once evidence is actionable
- GitOps
- Infrastructure as code
- OCI artifacts
- Build provenance
- Contract tests
- OpenTelemetry
Twelve product teams shared one platform team, and nearly everything went through a ticket. A standard preview environment took 5 business days from request to first use. Releases mostly worked, but each one involved someone asking whether tests had passed, someone else copying an image reference, and an operator confirming which configuration belonged in which environment. Reconstructing what had shipped meant searching chat history.
The platform team handled about 120 standard environment requests a month by hand, each a small variation on the last. The unusual requests that needed real judgment waited in the same queue behind them.
The ticket queue was the bottleneck
Most of those 5 days were waiting, not work. A request sat until someone picked it up, then configuration got copied from an older environment, then an access approval came through, then the environment went back to the team. Automating that sequence as it stood would only have made the same mistakes faster. We had to change what a standard request was.
A paved road for the common case
We turned the standard environment into a versioned template that bundled service identity, test data, a resource budget and an expiry date. Teams picked the supported template instead of building another one-off setup.
Checks ran before anything was provisioned: policy, identity and budget. If a request passed, the environment was ready in about 20 minutes and removed itself when it expired. If it failed, the team got a specific reason right away rather than a ticket bouncing back later.
Self-service didn't mean unrestricted. Sensitive data access and production changes needed the same approvals as before. What changed is that the ordinary requests stopped entering a manual queue, so the platform team's time went to the cases that needed a person.
One artifact per release
The release path got the same treatment. We wanted to remove repeated evidence-gathering and copy-pasting while keeping every point where someone makes a decision.
Claim. Each change recorded its service, owner, intended behavior and the evidence that would make it acceptable. Changes to shared contracts listed their consumers before anyone wrote code.
Build once. A reproducible build produced an immutable image digest with provenance, a dependency inventory and test results attached. Every environment received that same artifact; nothing was rebuilt per environment. Tags helped people find things, but the digest was the identity.
Review the exact artifact. Reviewers approved a specific digest and configuration change for a named environment. Once approved, the author couldn't swap in a different artifact.
Reconcile through GitOps. A standard GitOps controller converged each environment toward the approved state and reported drift. It applied rollout bounds but didn't judge whether a change was a good idea; that stayed with the reviewer. Separate identities published artifacts, approved promotions and applied them, so a compromised stage didn't hand over the others.
Watch the release, not the cluster. Errors, latency and a few business checks were tagged with the release identity. New versions started as a canary. If telemetry was missing, expansion paused until an owner made a call. Once the evidence pointed at a release, the team had a 10-minute budget to decide on rollback. That's a decision budget, not a promise about total recovery time.
Roll back to something compatible. Rollback restored a known artifact and its matching configuration through the same reviewed path. Schema changes used expand-and-contract migrations so old and new versions could run side by side. Redeploying yesterday's binary on top of a destructive migration can make an incident worse, so those migrations needed an explicit forward fix or restore plan.
Adopting it one service at a time
We started with one service whose ownership and recovery were already clear. Before onboarding anyone else we rehearsed a failed gate, a rejected digest, an unhealthy canary and a restore. Emergency changes stayed possible, as a separately approved event with an owner and an expiry.
Once the new path worked, we deleted the old promotion scripts. Leaving them around would have given the platform two normal paths, and people would have used whichever one felt easier that day.
Results
| Measure | Before | After |
|---|---|---|
| Standard environment lead time | 5 business days | 20 minutes |
| Manual work per release | 45 minutes | 12 minutes |
At 20 releases a week, the release change freed 47.63 engineering hours a month. At the client's loaded rate, after the extra running cost of the new path, that's $5,253.75 a month of net capacity. The 12 minutes that remain include review, which we kept on purpose.
No reliability benefit is counted in it, because pricing incidents that didn't happen would rest on guesses.
The environment change shows up on the calendar rather than in hours saved: across 12 teams and 120 requests a month, nobody loses a test window to the queue any more.
Client name and identifying details are left out.