All research

The Abstract Machine

Essay11 min read
  • program-synthesis
  • dsl
  • deleuze
  • software-architecture

Why we stopped writing solvers and started writing the thing that writes them

A small brass-and-card machine at one end of a workbench, the ordered parts of a half-built second copy laid out along the rest.
The abstract machine generates concrete, deterministic solver assemblages without being any of them.
Video overview1:00
All Videos

There is a moment in every systems project where the architecture tells you the truth about itself, and it usually does so through a settings panel.

Ours arrived while building a merchandising solver for grocery. The brief sounded singular: given a category, decide how it should live on a shelf. Tea was first. We wrote a solver, and it was good. Pulses came next, and the obvious move was reuse - same solver, different attributes, different weights, a few new checkboxes. Two categories in, this felt like engineering discipline. By the fifth category the checkboxes had become filters, the filters had become modes, and the modes had begun interacting with each other in ways nobody had designed. Somewhere around category fifteen we were no longer configuring a solver. We were negotiating with one.

The endgame was visible from there. Grocery is not one problem wearing fifty costumes. It is ninety-one micro-categories, each with its own physics (a theme explored further in The Plural Shelf). Tea is a loyalty category where brand blocks matter more than velocity. Pulses are a commodity wall where price laddering does the work. Biscuits are an impulse engine where eye-level real estate is the entire game. A universal solver that honors all of these truths simultaneously is not a product. It is a constitution, and constitutions are expensive to amend.

So we asked the heretical question: what if it is simply cheaper to write a solver per category?

Historically, this was an absurd position. Fifty solvers meant fifty codebases, fifty maintenance burdens, fifty opportunities for drift. The economics of software have always pushed toward the general, parameterized artifact precisely because human authorship is the scarce resource. You amortize the expensive human across as many use cases as possible, and you pay for that amortization in configuration complexity.

But human authorship is no longer the scarce resource. Models write competent code on demand. And that single change in the cost structure inverts the entire argument.

Axonometric cutaway explainer diagram contrasting a monolithic solver with a vocabulary-driven program synthesis pipeline generating deterministic category scripts.
Figure 1: Program synthesis with a verifier loop replaces monolithic parameterized solvers with specialized, enshrined scripts.

The vocabulary is the product

Here is the system we built instead, described plainly.

We did not write solvers. We wrote a vocabulary - a constrained language of merchandising primitives. Facings, shelf bands, velocity, adjacency, blocking, sell-through, hero placement. The vocabulary is small, precise, and grounded: every term in it corresponds to something measurable on a real shelf and scorable by a real rubric. If a desired outcome can be expressed in this vocabulary, the system can pursue it. If it cannot, the vocabulary is incomplete, and extending the vocabulary is the actual product work.

When a new category arrives, no human writes its solver. The ask is expressed as a sentence in the vocabulary. A model generates a candidate script. The script runs, a verifier scores the outcome against the rubric, and the loop iterates - ten, twenty times - until the outcome converges. Then, and this is the step everything hinges on, the winning script is enshrined. Frozen. It becomes a deterministic, auditable, boring artifact that will produce the same planogram from the same inputs every single time, at the cost of ordinary compute, with no model anywhere in the runtime path.

Two clients merchandise seasonal ranges differently? Script A and script B, both enshrined, both deterministic, neither pretending to be a special case of the other.

The literature has names for every piece of this, and it is worth being precise about the lineage, because none of the pieces is new. Only their economics are.

The constrained language is a domain-specific language, and the proof that a tight DSL makes synthesis tractable shipped inside Excel over a decade ago. Sumit Gulwani's FlashFill gave Excel a tiny vocabulary of string operations and a synthesizer that finds, from two or three examples, a program in that vocabulary matching the user's intent.1 Nobody at Microsoft tried to write a universal string-transformation solver with checkboxes. They wrote a grammar and let programs be found within it. FlashFill is the existence proof that the vocabulary, not the solver, is the durable artifact.

The generate-score-iterate loop is program synthesis with a verifier in the loop. The formal-methods world calls the pattern CEGIS - counterexample-guided inductive synthesis, from Armando Solar-Lezama's sketching work - where a candidate program is proposed, a checker finds where it fails, and the failure sharpens the next proposal.2 Our version swaps the SAT solver for a model and the formal spec for a merchandising rubric, but the shape is identical: propose, verify, refine, converge. The reason this works for us where it fails for most teams is that we already had the verifier. Years of scoring real shelves gave us a rubric that can grade an outcome without a human in the loop, and a synthesis loop is only as good as the judge inside it.

The freezing step has the most beautiful ancestry. Kevin Ellis and colleagues' DreamCoder (2021) at MIT synthesizes programs to solve tasks, then, in what the paper literally calls a sleep phase, abstracts the recurring fragments of its solutions into a growing library that makes future synthesis cheaper. Voyager (Wang et al., 2023) is the model-era incarnation: an agent dropped into Minecraft that writes code to accomplish tasks, tests it against the world, and stores every working program in a skill library for deterministic reuse. When it needs to craft a pickaxe again, it does not re-reason. It calls the function it already wrote. That is enshrinement, verbatim.

And the economics are amortization, which is where Andrej Karpathy's Software 2.0 and 3.0 framing earns its keep.3 Software 1.0 is code humans write. Software 2.0 is behavior learned into weights. The Software 3.0 conversation mostly celebrates prompts as the new programs, models reasoning at runtime, and that is precisely the trap. A model in the runtime path means every invocation is slow, costly, and stochastic. Our position is more disciplined: the model is the compiler, not the runtime. Prompts do not replace code here. Prompts manufacture code, which then behaves exactly like Software 1.0 — machine-readable, then machine-run — fast, cheap, deterministic, auditable. Synthesize once, expensively. Execute forever, for the price of ordinary compute.

But the name I keep returning to comes from somewhere else entirely.

Deleuze in the aisle

Gilles Deleuze and Felix Guattari spent A Thousand Plateaus attacking a picture of thought they called arborescent - the tree, the taproot, the master trunk from which all branches derive. Our old universal solver was arborescent to the bone. One genetic axis, fifty categories hanging off it as leaf nodes, every new truth about tea or biscuits forced to route through the trunk and pay tribute to the general case.

The tempting move is to call the new system rhizomatic - their word for structures with no center, where any point connects to any other point, where you draw fresh maps instead of tracing copies. And the synthesis loop genuinely is that. Twenty candidate scripts are twenty traversals of possibility space, none derived from a master template, each entering the problem from its own middle.

But the rhizome resists exactly the move our system treasures most. A rhizome, for Deleuze, never settles. It deterritorializes perpetually. The moment we freeze a winning script into a fixed, reusable artifact, we have done the un-rhizomatic thing: we have captured a line of flight and stratified it. And we do this on purpose, because a merchandising system that re-derives its logic on every invocation is a system that cannot execute at speed, cannot be audited, and cannot promise a client the same answer twice. Determinism after discovery is not a compromise. It is the entire commercial thesis.

Deleuze has a better concept for this, and it is the one the system deserves to be named after. The abstract machine is his term for the diagram that generates concrete arrangements without being any of them. It is not a solver. It is the thing from which solvers are actualized. Each enshrined script is what he would call an assemblage - a contingent arrangement of heterogeneous parts (this client's seasonal logic, that vocabulary, this verifier) held together not by essence but by the fact that it works, for now, for this shelf.

Technical blueprint diagram illustrating the abstract machine model: a virtual space of vocabulary primitives actualizing deterministic assemblages.
Figure 2: The Abstract Machine as a diagram of virtual possibilities actualizing concrete, deterministic assemblages.

The virtual and the actual complete the picture. The vocabulary defines a virtual space: every solver that could exist, real in its structure but not yet written. Each synthesis event actualizes exactly one of them. The virtual is not a library of pre-written answers waiting to be retrieved. It is a generative capacity, and the vocabulary is its grammar.

The part Deleuze would have enjoyed

There is one more mechanism, and it is the one that turns a cache into a compounding asset.

After enough solvers are enshrined, patterns recur. Anchor the hero SKU at eye level and cascade by velocity - this fragment appears in the tea solver, the biscuit solver, the beverages solver, each time re-synthesized from scratch. The system's job is to notice the recurrence, abstract the fragment, and promote it into the vocabulary as a named primitive. The next solver that needs it does not re-derive it. It composes it.

This is DreamCoder's deepest lesson, the one that separates its sleep phase from mere caching: the system does not just remember whole solutions, it compresses them, factoring out the shared abstractions and promoting them into the language itself. Ellis and colleagues showed that this is what makes the fiftieth task cheaper than the fifth. The same holds on our shelves. The vocabulary is not static. It grows richer with every solver the system births, and synthesis gets cheaper per category rather than linearly costly. Solver number fifty is not a fresh five-hundred-line search. It is a five-line composition of primitives that fifty predecessors paid to discover. The actualized history folds back into the virtual capacity. The strata feed the plane they crystallized out of.

That is the design in one sentence: a machine for converting exploration into assets, where the assets teach the machine to explore more cheaply.

What changes when the machine is abstract

The practical consequences are larger than they first appear.

Product scope inverts. You are no longer asking "what features should the solver have" but "what should the vocabulary be able to say." Feature requests become vocabulary requests, and vocabulary is far easier to reason about, version, and govern than a lattice of interacting checkboxes.

Maintenance inverts. A bug in an enshrined solver is fixed by re-synthesis against the same sentence, not by a human archaeologist excavating a general-purpose codebase to understand why the pulses mode breaks the tea mode.

And competition inverts. Anyone can call a model and generate code. Almost nobody has the three things the whole lineage, from FlashFill to DreamCoder to Voyager, says actually matter: a grounded vocabulary, a verifier that scores real outcomes, and a growing library of enshrined primitives distilled from thousands of live facilities. The model is a commodity. The abstract machine - the diagram that turns sentences into deterministic solvers - is not.

We spent years trying to build the perfect solver. The lesson of the checkboxes is that the perfect solver was never the product. The product is the machine that makes solvers, the vocabulary it speaks, and the library it remembers. Everything else is an assemblage, working for now, on this shelf, exactly as designed.

Footnotes

  1. Gulwani, S. (2011). Automating string transformations in spreadsheets using input-output examples. Proceedings of the 38th ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages (POPL '11), 317–330. ACM, Automating string processing in spreadsheets using input-output examples.

  2. Solar-Lezama, A. (2008). Program Synthesis by Sketching. PhD dissertation, University of California, Berkeley. people.csail.mit.edu.

  3. Karpathy, A. (2017). Software 2.0. Medium, Software 2.0.