← All writing

The Spec That Runs

TL;DR: For fifty years, software specifications have described systems that someone else had to build, and the description and the implementation have drifted apart from the day the first line of code was written. Every major attempt to close that gap (CASE tools, UML/MDA, code generators) failed the same way: they generated code, the code got edited, and the spec became a lie. ADL takes the other path. The specification is not compiled into an application; it is interpreted as the application, by a runtime called Kalos. There is no generated code to drift, because there is no generated code. This article explains the axis every specification technology sits on (description versus execution) and why we think interpretation, not generation, is the way off it. It’s also the frame for a series comparing ADL to OpenAPI, SQL, GraphQL, JSON Schema, BPMN/DMN, UML, and EXPRESS/STEP.


The oldest gap in software

Every non-trivial system has two representations: what we say it does, and what it actually does. The specification and the implementation.

The specification might be an OpenAPI document, a UML diagram, a JSON Schema, a BPMN chart, a data dictionary, a Confluence page. The implementation is the code. And from the moment both exist, they are two artifacts, maintained by different processes, under different pressures — and they diverge. A field gets added in code but not in the schema doc. A permission check gets patched in a hotfix and never makes it back to the design. Six months in, the specification describes a system that no longer exists.

This isn’t a discipline failure. It’s structural. When the spec and the implementation are separate artifacts, keeping them synchronized is unpaid work with no forcing function, and unpaid work without a forcing function does not get done. Everyone who has opened a “current architecture” diagram and laughed knows this.

The industry has responded to this gap in three ways. It’s worth being precise about all three, because ADL is a bet on the least-traveled one.

Response one: describe better

The first response says: the spec can’t be enforced, so at least make it precise. This is the lineage of OpenAPI, JSON Schema, UML, and EXPRESS/STEP: genuinely rigorous description languages, some of them (EXPRESS especially) far richer than they’re given credit for.

Precision helps. A machine-readable spec can drive validation at the boundary, generate client types, and catch some drift in CI. But description-side rigor never solves the core problem, because the behavior still lives somewhere else. OpenAPI can say an endpoint exists and what shape its payloads take; it cannot say what happens when the request arrives: who may call it, in which states, under which business rules. Those semantics live in hand-written code the spec cannot see. The spec describes the surface; the system is the depth.

Description languages are contracts awaiting an implementation. The contract can be exact and the implementation can still be wrong, or right today and wrong after the next sprint.

Response two: execute a layer

The second response says: pick one layer of the system and make its specification directly executable. This actually works, and it’s why the two most successful “specs” in software history are SQL and, more recently, workflow engines.

SQL is a declarative specification of storage — and it executes. You don’t describe a table and then implement it; the CREATE TABLE statement is the table. Nobody talks about “schema drift between the DDL and the database,” because there is only one artifact. That is exactly the property we want.

BPMN and DMN did the same for the process and decision layers. A Camunda-class engine doesn’t treat a BPMN diagram as documentation; it runs it. Same property, different layer.

But each of these executes only its layer. SQL knows nothing above the table: it has no concept of your application’s users (database roles are not your auth system), no API, no state machines, no cross-record business policy expressed as policy. A BPMN engine owns process orchestration but assumes the data layer, the API, the identity system, and the services it orchestrates already exist, built, by hand, by someone. In both cases, the application layer (the part where most of the behavior, most of the bugs, and most of the drift live) remains hand-written code described by documents that go stale.

So the industry has proven that executable specifications work. It has just never shipped one that covers the whole application.

Response three: generate the rest

The third response is the famous one, and the graveyard is full of it. If the spec is precise, why not generate the implementation from it? CASE tools promised this in the eighties. Model-Driven Architecture promised it in the two-thousands with UML as the source language. Scaffolding generators promise a weaker version of it today.

Generation fails for one structural reason: the moment generated code exists, it is code, and code gets edited. The generated 80% is never the whole system, so developers modify the output, and the instant they do, regeneration becomes destructive. Now you’re maintaining the model and the code, they disagree, and the model loses, because the code is what’s running in production. MDA didn’t die because modeling was a bad idea. It died because generation reintroduces the two-artifact problem it was meant to solve, with interest.

There is a modern echo of this worth naming plainly. AI coding agents have collapsed the cost of producing code. An agent will happily emit fifty thousand plausible lines from a prompt. But they have not collapsed the cost of owning code: reviewing it, securing it, migrating it, understanding it three years later. Generation at agent speed doesn’t shrink the liability surface of a system. It grows it faster than any technology in history. If the spec-to-code path was a trap at human generation speeds, it is a bigger trap now, not a smaller one.

The fourth path: interpret the spec

ADL (Application Definition Language) is a bet on the path the industry mostly skipped: don’t describe the application, don’t generate the application — interpret the application.

An ADL file is a YAML document that defines a domain: its resources and types, relationships, indexes, role-based permissions down to the field level, state machines with guarded transitions, validation rules, computed fields, cross-record business rules, history, notifications, scheduled tasks, workflows, and the rest. Kalos, a C++ microkernel runtime, reads that document and becomes that application: it derives the database schema (and migrates it, atomically, when the spec changes), registers the REST endpoints, enforces the permissions and state machines on every request, evaluates the rules before every write, and emits an OpenAPI document describing the surface, as a byproduct, not as an input.

One binary. No generated code. The YAML is the application.

This is the same property SQL has, extended from the storage layer to the whole application layer. There is no drift between the specification and the system, because there is only one artifact. When you want to know what the permission model is, you don’t read the code and hope the wiki agrees. You read the spec, and the spec is, by construction, what’s running. When an auditor asks how the system enforces an enrollment cap, the answer is four lines of YAML citing the regulation, and those four lines are the enforcement.

The obvious objection is that this only matters if the language is rich enough. A spec that covers CRUD and stops is scaffolding with better marketing, and the gap between the spec and the real system just moves into a pile of custom code next door. This is the make-or-break question for any interpreted-spec approach, and it’s why ADL’s surface area is large: around forty chapters of defined engine semantics. The claim is not that a data model can be interpreted; SQL settled that. The claim is that access control, lifecycle, validation, business policy, process, and integration can be: that the whole behavioral core of a business application is specifiable, and that what remains genuinely irreducible is small.

What this costs

An honest version of this argument has to state what you give up, so here it is.

You give up the open world of arbitrary code. ADL is a closed language: a fixed type system, a defined expression language, a bounded set of behaviors. Logic that doesn’t fit the declarative model drops into sandboxed Lua hooks, a real escape hatch, but a hatch, not a door. If your application is mostly novel algorithmic behavior, an interpreted spec is the wrong tool. ADL’s bet is that most business applications are not that: they are data, rules, access, and lifecycle, in configurations that a sufficiently rich language can express. The bet is explicit, and it doesn’t cover all software.

You take on a runtime dependency. The spec only runs because Kalos interprets it, and Kalos is one vendor’s runtime, not an ISO standard. That’s a real adoption cost, and it’s why the exits matter and are deliberate: your data lives in plain SQLite or PostgreSQL, your API surface is standard OpenAPI 3.1 that any client generator consumes, and the same ADL document can be fed to a code generator to produce a conventional FastAPI/SQLAlchemy codebase if you ever want to leave the runtime entirely. Generation, in this architecture, is the emergency exit, not the operating mode. That inversion is the whole design.

You inherit the spec’s correctness problem. An interpreted spec can’t drift from the implementation, but it can simply be wrong: the wrong permission on the wrong field is enforced with perfect fidelity. What you gain is that the error is findable: it’s one line in one reviewable document with defined semantics, not a behavior emergent from fifty thousand lines. In an era where AI agents author systems, this may be the property that matters most. A spec is small enough for a human to actually review and precise enough for a runtime to actually enforce. A generated codebase is neither.

Where everything else sits

This series will place each familiar technology on the description-execution axis, one article at a time, and in most cases the honest relationship isn’t rivalry. ADL emits OpenAPI and SQL rather than competing with them. It subsumes JSON-Schema-class validation and extends it with the context a lone document can’t have: identity, state, the rest of the data. It contains a workflow and rules layer that overlaps BPMN/DMN in scope but not in situation, since those engines orchestrate systems you’ve already built, while ADL is how the system gets built. It inverts the lesson of UML and MDA: model-driven development failed on generation, not on modeling. And it inherits from EXPRESS/STEP, which proved thirty years ago that rich declarative modeling of complex domains works, and never shipped with a server.

Description versus execution. Every technology in this series picked a spot on that axis. ADL’s spot is simple to state and expensive to build: the specification runs.

Next in the series: ADL vs OpenAPI — why you can generate an OpenAPI spec from an application, but never an application from an OpenAPI spec.