All posts

Devlog

Two languages, one mechanic — why we let the design doc and the code disagree

A designer writes a single sentence: *the moon orbits the planet.* Down in the engine, that same mechanic is an angle, a parent reference, and a loop that advances it once per frame. Two descriptions of one thing, and they share not a single word. Most build pipelines treat that gap as a defect to be hammered flat — make the doc match the code, or the code match the doc. We built ours to keep the gap open on purpose. This post is about why — and about what changes when the design, not the code, becomes the thing a human writes.

What spec-driven design actually is

For most of software's history the source code was the contract. It was the one authoritative statement of what a system did — you read the code to know the truth, and every other description of it drifted the moment it was written. That held up while a human wrote every line. It strains the moment an AI writes most of them.

Spec-driven design splits the agreement into two levels. Underneath, there is still source code — the exact, low-level instructions the machine runs. Above it sits a high-level contract written in human terms: what a mechanic is, how it should behave, what counts as correct. The low-level layer is the deal between the engine and the machine. The high-level one is the deal between a *person* and the *AI* that produces the machine's part.

That second contract is the one that matters more as agents take over the typing. When an AI can turn intent into a working implementation, the scarce skill stops being the production of code and becomes the precise statement of intent — clear enough that an agent can build it and a human can check it. The spec is where that intent lives. It is the most human layer in the stack, and increasingly the only one a person writes by hand.

A high-level contract like this earns its place by what it gives you:

  • A source of truth that outlives the code — implementations get rewritten, tuned, and thrown away; the intent behind them is the part worth keeping.
  • A review surface worth a human's time — you review what a mechanic should *do*, not every line of *how* — higher leverage, and far harder to wave through.
  • One place everyone can point at — a designer, a reviewer, and an AI agent can all agree on the spec without any of them having to read the others' native vocabulary.
  • Something to hold the AI to — when the implementation and the intent disagree, the high-level contract is what decides which side is wrong.

That high-level contract is the spec. The machine side beneath it — the part we just called source code — turns out to have a shape of its own. Three layers, in fact.

Three layers, three readers

Every mechanic in our games passes through three representations, and each one is written for a different reader.

  • The spec — A short document a *designer* reads and writes, in plain design language.
  • The contract — A structured declaration the *build system* reads — what data a mechanic touches and what events it raises.
  • The body — The implementation the *CPU* runs every frame.

The trick is that these three are not translations of one canonical text. They are three native documents in three vocabularies, joined by a workflow rather than forced into lockstep. An AI agent moves between them — and that translation, in both directions, is the actual product.

Layer one: the spec, in plain design language

A spec describes one mechanic the way a designer thinks about it: named states, behaviours, what the player sees and feels. *A moon orbits its planet. Fire it and it becomes a projectile. A fired moon never returns to orbit.* That is a complete, useful spec. There is no formula in it, no data layout, no engine term — and that is enforced, not encouraged. The moment implementation shape leaks into the spec, the whole arrangement collapses, because the spec stops being something a designer can own.

A spec is allowed to describe a known bug, an open question, or a decision nobody has made yet. It is a design document, not a promise that the code already behaves this way. Open questions can sit open across many sessions — capturing the uncertainty is part of the job.

Layer two: the contract

Between the designer's prose and the running code sits a structured contract: a precise, machine-readable statement of what each mechanic reads, what it writes, and what it announces to the rest of the simulation. This is the dependable bridge — the one layer that is unambiguous to a machine. It is also where the design-to-implementation map stops being one-to-one. Several distinct design mechanics can collapse into a single unit of work here, because what reads cleanly as five separate ideas to a designer is often most efficiently run as one.

Layer three: the body, fused for speed

At the bottom is the code that runs each frame. Where a designer sees a handful of independent mechanics, the engine often runs them in a single pass over the same data — several behaviours resolved together rather than walking the same state once per mechanic. Touching the same data once and doing several things with it beats touching it many times; on a per-frame budget that difference is the whole game. The pipeline scaffolds this layer — it sets up the shape and the checklist — but a human writes the real logic. Nothing here is auto-generated and shipped blind.

the same mechanic, two languages
designer's spec   the moon orbits its planet. fire it and it becomes
                  a projectile; a fired moon never returns to orbit.

engine's body     each frame, advance every orbiting moon by its angle,
                  and resolve any that were launched this frame into
                  free-flying projectiles. fired is a one-way door.

Same mechanic. Two vocabularies. Neither is wrong; neither is the master copy.

Why we let the layers disagree

Here is the principle the whole system turns on: a mismatch between the spec and the code is welcomed, not flagged. When the designer says *orbit* and the code says *angle around a parent*, that is not drift — it is the translation working correctly. Forcing the two to use the same words would mean either burdening the designer with engine concepts or polluting the engine with design metaphors. Instead, the agent absorbs the impedance. Its entire job is to speak both languages and keep them honest to the same behaviour while letting them stay worded for their own readers.

The corpus writes its own to-do list

Specs are not planned up front in a big design bible. They accrete. Writing one almost always surfaces the next: spec a collection mechanic and you immediately raise the question of what happens when the thing being collected is full. That question gets written down inside the spec as an open thread, and it becomes the seed of a future spec. The design corpus extends itself — each finished mechanic naming the ones it implies. The abstractions we end up with are the ones real mechanics demanded, not the ones we guessed we would need.

It runs in reverse, too

The pipeline is bidirectional. When a developer changes the code, the spec is reconciled back to the new behaviour — but renamed internals are deliberately *not* pushed up into the design language. If a code change happens to settle a question the spec had left open, that resolution is lifted into the spec as a now-decided point. And because every layer has a stable way of referring to the others, it is cheap to detect the two kinds of gap that matter: code that exists with no design intent recorded, and design intent with no code behind it yet. Every change is shown as a full diff and signed off by a human before it lands. Nothing is silently applied.

Why a document, not a config file

The spec is a human-readable document, not a data file, and that choice is doing real work. A designer has to be able to open it, read it like prose, and reason about a mechanic without parsing structure. The machine-facing contract is a separate artifact precisely so the human-facing one can stay readable. Keeping the design layer as a document — and keeping code markup out of it by rule — is what protects the divergence the whole system depends on.

The payoff

Designers describe mechanics in the language of design. The engine runs them in the language of the machine. An AI agent stands between the two, translating in both directions and flagging only the gaps that actually mean something. The result is a design process where the document and the code are each allowed to be the best version of themselves — and a knowledge base of mechanics that grows itself, one spec naming the next.