← Writing

An environment for every task

Before I wrote a line of the Cyberbase Development Kit, everyone on the team was running a different platform. Same repos, same code — but every laptop had drifted into its own configuration and its own half-remembered setup steps. Bugs appeared that only existed on one machine. Worse, bugs disappeared on one machine and shipped anyway. We were making decisions on top of environments we couldn't trust.

The fix most teams reach for is documentation — a longer README, one more setup checklist. I don't believe in that. If running the system correctly depends on a human remembering the steps, the system is already broken. So I made a bet: standing up the whole platform should be a single command, and it should produce the same world every time.

Everyone's laptop was a different planet

The concrete failure was false positives. Someone would “reproduce” a bug that was really just their stale config. Someone else would sign off on a fix that only worked because their local state happened to line up. The confusion about how to even start the platform was the surface symptom; the real cost was that we couldn't separate a true result from an artifact of one machine.

What didn't work

We tried the usual two. A bash script got us running, but a script is not a toolkit — it does one thing, and every new need bolts more mess onto it. Docker Compose was cleaner, but it had a limit that turned out to be fatal for how we actually work: it gives you one world at a time. I couldn't spin up a second, isolated environment to review a pull request while my main dev environment kept running. That single missing capability turned out to be the whole game.

The real requirement was concurrency

What I actually needed wasn't “run the platform.” It was run many platforms at once, each isolated. My dev environment, a throwaway environment to check a PR, another to reproduce a customer issue — all live at the same time, none able to corrupt another. Every cdk environment runs its own infrastructure, its own database and state, fully separate from every other. Spinning one up is a command; throwing it away is a command.

# keep developing while a PR gets its own clean world
cdk env create pr-1841 --mode dev
cdk env start pr-1841
# your dev environment stays untouched the whole time

Why real Kubernetes

The contrarian choice is running a real Kubernetes cluster locally instead of something lighter. The reason is parity. Cyberbase runs on Kubernetes in production, so the environment that reproduces a production bug should be shaped like production — same topology, same failure modes. An environment that's convenient but different is exactly how you manufacture the false positives I was trying to kill. One local cluster also hands me many cheap, isolated namespaces, which is what makes the concurrency above practically free.

Config can't drift if it has two owners

Reproducibility dies in configuration, so cdk splits ownership of it in two. Each product repo checks in a contract that declares the shape of its environment — the variable names, where each value comes from, whether it's required. cdk owns the values. A variable is one of five kinds: a literal constant, a value derived from a formula, an infrastructure fact like a service host, a user secret, or a user config. The repo says what it needs; cdk decides what it is.

The point of that split is that neither side can quietly drift. A repo can't hardcode a secret, and cdk can't invent a variable a repo never asked for. Configuration stops being folklore passed between developers and becomes something checked in and generated.

If running the system correctly depends on a human remembering, the system is already broken.

The part that isn't here yet

I'll be honest about where this points, because the destination is why the design looks the way it does. The goal is autonomous development: a fleet of AI agents, each on its own isolated machine, each able to spin up a full Cyberbase environment, do real work against production-shaped infrastructure, and tear it down — many of them at once, with data isolation per task so they never collide. That fleet isn't running today. What is running today is the substrate it needs: reproducible, isolated, concurrent environments, one command each.

That order is deliberate. You don't get agentic development by pointing models at a codebase they can't safely run. You get it by first making “a clean, correct, throwaway environment” a primitive — cheap, isolated, and identical every time. Build the ground, and the fleet has somewhere to land.