· Tim Kleier with Claude (Anthropic) · Engineering · 6 min read
The Future of Programming
The Future of Programming: The Abstraction Layers Between Intent and Execution
Today an LLM leaps straight from a prompt to code, deciding everything in between and writing none of it down. The next era of software engineering fills that gap with real abstraction layers: semantics, ontology, operational model, and execution model. Every handoff checkable, nothing guessed.

“A customer should be able to pay an invoice.”
Hand that sentence to a coding agent today and you’ll get a pull request back in a few minutes: a migration, an endpoint, a service function, a couple of tests. Sometimes it’s exactly right. Sometimes customer turns out to mean the billing contact, while the rest of your system has always meant the parent account — and you find out six weeks later, in a reconciliation report.
Both outcomes come out of the same process. That’s the problem.
Between that sentence and that code, a dozen decisions got made. What a customer is. What “pay” does to an invoice. What happens when the amount is short, or paid twice, or paid against an invoice that was already voided. The model decided every one of them, and none of them were written down anywhere you can point at.
The Current Paradigm
That’s the leap: one jump, from a prompt straight to code.
One jump from a prompt to code. The four layers in between still get decided — they just rarely get written down, so nothing can review them, diff them, or hold a correction. Each one is practiced somewhere in the industry. None of them is the default.
The outcome someone wants, in their own words. No structure, no types, no system boundaries — just a goal.
ProblemThe outcome lives in one person’s head. What counts as done gets settled after the code exists — which is also when it gets argued about.
SolutionUser stories, PRDs, tickets, acceptance criteria. Universal — the one layer above code everybody writes. But it stops at prose, so the handoff is a person retelling it. Nothing below can read a ticket.
The vocabulary. Which words carry a specific meaning here, and exactly what each one refers to in this business.
Problemcustomer quietly means the billing contact in one module and the parent account in another. Two correct implementations, one wrong system.
SolutionDomain-Driven Design’s ubiquitous language, data catalogs, dbt docs, schema registries, controlled vocabularies. But the glossary and the code are two artifacts kept in step by hand. Renaming a term in the wiki changes nothing downstream.
The entities and the relationships between them — what can exist, what each thing holds, and what it points at.
ProblemRelationships get rediscovered feature by feature. The schema ends up as an archaeology of past tickets rather than a model of the business.
SolutionOWL, RDF and SPARQL go back decades; knowledge graphs run production search; Palantir built a company on programming against “the ontology.” But it reads as a modeling specialty rather than a step in the build, so most teams jump straight to a database schema — which answers how to store this, never what this is.
Operations — the behavior the system allows. Each one names an actor, a target, its preconditions, and the state it leaves behind.
ProblemEvery consumer re-derives the rules for itself. The API permits a payment the UI forbids — and an agent will find that gap before your QA does.
SolutionBPMN, statecharts and XState, workflow engines like Temporal and Camunda, DDD aggregates and commands, policy rules in an authorization service. But each models one slice — a process, a state machine, a permission — and none of them holds the operation itself, so the rules stay scattered across all of them.
The surfaces an operation is reachable through, and the contract each surface honors.
ProblemThe tool description and the endpoint drift apart. An agent calls what it was told exists, not what actually does.
SolutionOpenAPI, protobuf, GraphQL SDL, AsyncAPI, JSON Schema — and now MCP tool definitions. But each describes a surface, not the operation behind it. Four specs for one behavior, hand-kept, each free to disagree.
The actual instructions: control flow, persistence, validation, error handling.
ProblemNot a missing layer — the opposite. It is the one thing we have always been forced to write down, which is exactly why it absorbs every decision made above it.
SolutionUniversal. Nobody ships without it. But when it is the only written artifact, reading it cannot tell you which lines are implementation and which are a definition nobody stated.
The chosen substrate — framework conventions, libraries, data store, deployment target.
ProblemRarely inferred, often premature. A stack picked before the operations are known will quietly bend the operations to fit it.
SolutionFramework conventions, dependency manifests, infrastructure-as-code — well documented and loudly argued about. But it tends to be the first decision instead of a late one, so the substrate shapes the model rather than serving it.
Bytecode, JIT, syscalls, silicon. The layer nobody opens.
ProblemNone left. Decades of compiler work made this handoff so dependable it went invisible — and that is the bar every layer above it is still failing.
SolutionCompilers, interpreters, JITs. Fully standardized, fully mechanical, entirely invisible — the one handoff software has actually finished.
The four layers in the middle don’t disappear. They get decided — silently, once per feature, by whatever happened to be in context at the time. Three things follow from that, and all three cost money.
You can’t review what was assumed. The code shows you what the model did, never what it decided on the way. To recover the assumption about customer you’d have to infer it from which table the key points at and which case the tests quietly skip. That’s archaeology, not review — and it’s why the wrong assumption sails through. A diff built on a bad definition looks exactly like a diff built on a good one.
Corrections don’t stick. You tell the agent a customer is the parent account. It fixes the code. Next feature, next session, it guesses again — because there was nowhere for that correction to live. A correction with nowhere to live is a correction you will make again.
It isn’t repeatable. Run the same intent twice and you get two different implicit models. Run it across fifty features and you get fifty of them, each internally sensible, collectively incoherent. Consistency is precisely what a large system needs and precisely what one-shot inference can’t give you.
None of this is a model-intelligence problem, and here’s the test: a very good human engineer, handed that one sentence and no other context, would also produce something plausible and possibly wrong. Not from lack of intelligence. From lack of input.
The Missing Layers
So what input is missing? The bottom three layers you already have. The four in the middle are the ones that get inferred today instead of stated:
Semantics is the vocabulary — not a glossary in a wiki nobody reads, but the binding definition of which terms carry a specific meaning here and what each one refers to. An integration bug is usually two systems that never agreed on a concept.
Ontology is what exists and how it connects. Some of it leaks into your database schema, but a schema answers “how do we store this,” not “what is this.” The rest leaks into a GraphQL SDL, a protobuf file, an ORM class — each a real model of your entities, each bent toward the job it was built for. Four partial ontologies, no source, and when they disagree the winner is whichever one the running code happens to read.
The Operational Model answers: how does the system behave? What can happen, and by whom? Pay Invoice is performed by a Customer, against an Invoice in status Issued, and moves it to Paid when the amount matches what’s due. Without somewhere to say that once, operations dissolve into the code: a precondition here, a status transition there, a permission check three files away, a rule that survives only because someone remembered it in review.
The Execution Model is how an operation becomes reachable. The same Pay Invoice surfaces as a REST endpoint, an MCP tool, a workflow step, and a button. Written four times by four people, they drift apart immediately — and an agent will call whichever one it was told about, not the one that happens to be right.
None of this is hypothetical. Semantics is Domain-Driven Design’s ubiquitous language and the premise of every data catalog. Ontology has OWL, knowledge graphs running production search, and Palantir’s entire business behind it. The operational model shows up as BPMN, as statecharts, as Temporal and Camunda, as DDD aggregates and commands. Execution has OpenAPI and now MCP. The problem was never that the industry failed to figure these out — it’s that each lives in its own pocket, optional and disconnected, and nothing carries a definition from one layer into the next. That’s the difference between a practice and an abstraction layer.
The Future
Programming has always advanced the same way — add a layer, then engineer the handoff beneath it until the handoff gets boring. Assembly to machine code. C to assembly. Bytecode, JIT, linkers, query planners. Nobody debugs register allocation anymore, not because it stopped mattering, but because that handoff got engineered so thoroughly it went invisible. If you have no idea what bytecode or register allocation is, that’s precisely the point — those are the names of problems that got solved so completely you never had to learn them.
Above Code / Program, that work has barely started. Not because the layers are unknown — we just named all four — but because nothing carries a definition from one of them into the next. Here is the same ladder with that part built.
Every layer named and stated. Short hops an agent can take one at a time, each ending in an artifact you can read and check.
The outcome someone wants, in their own words. No structure, no types, no system boundaries — just a goal.
ProblemThe outcome lives in one person’s head. What counts as done gets settled after the code exists — which is also when it gets argued about.
SolutionWrite the outcome down: what should become possible, and what counts as done. Now "done" is agreed before the work starts rather than after it — and every term the sentence leans on becomes the next layer’s input.
The vocabulary. Which words carry a specific meaning here, and exactly what each one refers to in this business.
Problemcustomer quietly means the billing contact in one module and the parent account in another. Two correct implementations, one wrong system.
SolutionA glossary of binding terms: what each one refers to here, and what it explicitly is not. One definition of customer, read by both modules — so they can no longer mean different things without the diff showing it.
The entities and the relationships between them — what can exist, what each thing holds, and what it points at.
ProblemRelationships get rediscovered feature by feature. The schema ends up as an archaeology of past tickets rather than a model of the business.
SolutionAn entity model, declared up front: what exists, what each thing holds, and how they connect. The schema becomes a projection of that model instead of the only record of it — so relationships get read rather than rediscovered.
Operations — the behavior the system allows. Each one names an actor, a target, its preconditions, and the state it leaves behind.
ProblemEvery consumer re-derives the rules for itself. The API permits a payment the UI forbids — and an agent will find that gap before your QA does.
SolutionOne operation definition: actor, target, preconditions, resulting state, and the rules that must hold. Every consumer reads that definition instead of re-deriving it, so the API and the UI cannot disagree about what a payment is allowed to do.
The surfaces an operation is reachable through, and the contract each surface honors.
ProblemThe tool description and the endpoint drift apart. An agent calls what it was told exists, not what actually does.
SolutionEvery surface projected from the one operation — REST endpoint, MCP tool, queue consumer, button. Same preconditions, same effects, same errors by construction — the tool description can no longer promise something the endpoint does not do.
The actual instructions: control flow, persistence, validation, error handling.
ProblemNot a missing layer — the opposite. It is the one thing we have always been forced to write down, which is exactly why it absorbs every decision made above it.
SolutionImplementation and nothing else: control flow, persistence, validation, error handling. With the definitions living above it, the code stops being the only place they exist — a diff now shows what changed, not what was assumed.
The chosen substrate — framework conventions, libraries, data store, deployment target.
ProblemRarely inferred, often premature. A stack picked before the operations are known will quietly bend the operations to fit it.
SolutionThe substrate picked once the operations are settled: framework, data store, deployment target. The stack now serves the model rather than bending it — and replacing it later changes how the system runs, not what it means.
Bytecode, JIT, syscalls, silicon. The layer nobody opens.
ProblemNone left. Decades of compiler work made this handoff so dependable it went invisible — and that is the bar every layer above it is still failing.
SolutionNothing to build. Instruction selection, register allocation, inlining and vectorization already work this way. Billions of these decisions get made a day and reviewed by nobody — which is what every layer above is trying to earn.
Nothing is ghosted now. Every layer is an artifact you can read and correct, which turns each step between them into something a tool can check rather than something a model improvises. The four surfaces of Pay Invoice stop being four hand-written contracts and become four projections of one operation: same preconditions, same effects, same errors, by construction.
Layering changes where agents fit, too. Today we aim them at the widest gap on the ladder, which is also where their output is hardest to check; layered, the same work becomes short hops that each end in something readable — an agent proposes the vocabulary and you confirm it, derives the operations and you correct the two it got wrong. It runs the other direction as well: an agent that pays invoices and escalates tickets needs those same definitions to know what it’s permitted to do, and the layer that makes generation reliable is the one that makes delegation safe.
The future of programming isn’t an LLM that jumps further across abstraction layers; it’s a stack that makes the leap unnecessary, with new abstraction layers that are as dependable as the ones we’ve relied upon for decades.
We’re building DNA Codes for the parts of software nobody writes down — the vocabulary, entities, and operations a business runs on, modeled once and run everywhere. If that’s how you’re thinking about the problem too, join the waitlist — we’d like to have you involved.
Written by Tim Kleier with Claude (Anthropic). The argument, the claims and the last word are Tim Kleier's; the drafting and the editing were done together.

