Why Constrained Languages Make Better LLM Partners

Large language models can generate impressive volumes of code from a short description, but the quality of that output depends heavily on how well the intent is constrained. When a natural language request maps cleanly onto a small, well-defined set of concepts, generation becomes far more reliable. Domain-specific languages (DSLs) provide exactly that harness: a restricted syntax that eliminates ambiguity and gives the model clear boundaries to work within.

Two realities shape how LLMs should be used for building systems. First, no upfront specification can capture all the design decisions that emerge during implementation. The initial spec is a starting hypothesis, not a finished blueprint. Second, real design insight comes from writing code, not just reviewing it. Reviewing generated code can catch errors, but it rarely forces you to wrestle with where responsibilities belong or what boundaries to expose. Those decisions surface through the act of implementation, and the language you write in influences what you notice.

LLMs fit into this picture in two ways. Before the domain vocabulary is settled, they can act as brainstorming partners to explore design alternatives and identify useful abstractions. Once that vocabulary is encoded in a domain model and DSL, they become a natural language interface to those established constructs. DDD's ubiquitous language and the DSLs built on top of it become the shared contract between human intent and machine output.

What Makes DSLs So Reliable for LLMs

The pattern appears across well-known DSLs — Mermaid and PlantUML for diagrams, SQL for queries, Kubernetes YAML for infrastructure. These languages are deliberately narrow, expressing only a single domain's concepts. LLMs handle them remarkably well from plain English descriptions. The key observation is that a few in-context examples are usually enough to convey correct usage. General-purpose languages like Java offer countless valid ways to express the same intent; a DSL strips that variation away, making model output much more predictable.

For autonomous agent workflows, DSLs offer a second major benefit: they almost always ship with deterministic validation tools — parsers, JSON schemas, type checkers, compilers. An agent can generate candidates, run them through the validator, and fix issues from structured error messages, without human involvement. Errors like "you cannot select an action before choosing a client" are far more actionable than a stack trace buried in generated code.

These advantages hold only while the DSL remains small enough for a few examples to convey its full usage. Designing and maintaining a good DSL has real upfront costs, but the payoff is concentrated in well-factored languages backed by validators.

Putting the Model to Work

A concrete case illustrates both roles LLMs play. A tool built for creating presentations of distributed system scenarios uses a YAML specification for slide structure combined with step-annotated PlantUML diagram definitions. The YAML describes the presentation flow — which diagrams appear on each slide and how the steps unfold. A presentation on distributed system testing was produced end-to-end from natural language prompts against this specification.

@startuml
actor Alice

box "Cluster" #lightblue
participant athens
participant byzantium
participant cyrene
end box

'[step]
Alice -> athens: "title", "After Dawn"

'[step]
athens -> athens: save()

note right of athens
  state:
  title: After Dawn
end note

'[step]
athens -[#red]x byzantium: "title", "After Dawn"

'[step]
athens -> cyrene: "title", "After Dawn"

note right of cyrene
  state:
  title: After Dawn
end note

'[step]
athens -> athens: isQuorumReached()

'[step]
athens --> Alice: Success

@enduml
          

This PlantUML output is then referenced in the slide layer. A second simple request to create a slide YAML for the diagram with the title "Quorum Write Example" produces a valid spec that feeds directly into the presentation pipeline.

          - slide:
              title: "Quorum Write Example"
              diagram: "quorum-write"
        

The important detail is that the generated YAML is not arbitrary — the tooling and its spec are included as context in the prompt, guiding the LLM to emit valid structures the same tooling can consume unchanged. The complete specification for this system is maintained in the project's source repository.

Here the LLM served as co-designer while the DSL and its annotations were being shaped, then switched to natural-language interface once the specification existed. That division of labor — building the language first, then using the model against it — is the pattern that makes LLM-generated output trustworthy.

Why Distributed Systems Need More Than Prompts

Building a quorum-based key-value store or a consensus protocol like Raft or Paxos is notoriously difficult. Even with careful prompting or detailed specification files, the asynchronous runtimes leave too many implementation decisions open: threading models, networking patterns, storage coordination, retry behavior, and timing semantics all have to be resolved somewhere in the generated code. The real problem, though, is not generation complexity—it is verification complexity. The state space of possible thread interleavings, network delays, process pauses, and clock skew grows so large that systematic review and validation become nearly impossible. This is precisely why tools like Jepsen continue to find bugs in even the most hardened production systems.

A semantic model directly addresses this. Tickloom is a compact framework for constructing and testing distributed algorithms. Rather than providing a generic runtime, its abstractions embody specific design decisions about how a distributed process behaves. Every node runs in a single-threaded tick loop: each tick() increments a logical clock and processes pending work in a deterministic order—network, message bus, process, then storage. Time is expressed in ticks, not milliseconds. Messages are plain Java records, and cross-replica coordination is handled by a Replica base class that knows about peers, broadcasts, and quorums.

Because threading, timing, and network delivery are no longer open questions, the algorithm author focuses on actual protocol logic. A quorum replica is just a set of message handlers written in the framework’s vocabulary. The framework supplies types like Replica, quorumRequest, countResponseIf, MessageType, and Handler, so a prompt stays at the level of the protocol:

Using the Tickloom Replica abstraction, implement a quorum-based key-value store. A client GET should collect values from a majority and return the one with the highest timestamp (last-writer-wins). Apply a write locally only if its timestamp is newer than the stored one.

That description yields concrete, compilable code. The semantic model acts as the context: the prompt names concepts that exist as types in the codebase, so the LLM is not inventing a threading model or a networking layer—it is filling in protocol logic against a fixed, well-understood substrate.

Good Abstractions Are a Lighter DSL

A full DSL is not always necessary. A clean set of abstractions is a lighter version of the same idea, and a library’s named types and methods function as a vocabulary the LLM can be grounded in. Tickloom’s semantic model is really just four seams—Process/Replica for compute and message handling, Network for communication, Storage for persistence, and a logical Clock for time—and that decomposition does most of the work without introducing any new syntax.

This is why abstractions pair so well with LLMs. A prompt to “implement Raft as a Tickloom Replica” has a drastically reduced state space to explore, and an existing implementation like QuorumReplica can serve as a worked example in context.

A Test DSL for Failure Scenarios

Implementing an algorithm is one problem; exercising it with the right failure scenarios is another. The subtle bugs in distributed systems depend on specific orderings: a write replicates to one node, then a reader’s quorum shifts; a partition heals at the wrong moment; two coordinators’ clocks drift apart. Writing such a scenario directly against the testkit means manually juggling futures and explicit tick() loops. A clock-skew scenario written that way buries its intent—"Bob writes through Byzantium, Alice writes through Athens, a reader sees Bob’s value because Byzantium’s clock is ahead"—under dozens of incidental decisions about when to tick, how to encode bytes, or which factory overload to call. Every one of those decisions is a chance for an LLM to err and for a reviewer to miss it.

Tickloom therefore layers an internal DSL on top of its semantic model. The DSL’s vocabulary is the scenario itself—servers, clients, connectivity, actions, and faults—and it compiles down to a pure intermediate representation of Steps and Actions with optional ClusterEvents for faults. Failure descriptions read like English: partition(BYZANTIUM).from(CYRENE), reconnect(BYZANTIUM), delay(INTERNAL_SET_REQUEST).from(ATHENS).to(BYZANTIUM, CYRENE).byTicks(100). Progressive interfaces enforce the grammar through the type system, so declarations must occur in a valid order: a step cannot precede topology, nor an action a client selection. Malformed scenarios do not compile; since the DSL is built in Java, the host compiler provides validation for free and pins any error to the exact illegal step.

With the DSL in place, a natural-language description maps almost one-to-one to code. The scenario can remain entirely within the constrained DSL vocabulary:

Using the Tickloom scenario DSL, write a scenario reproducing the DDIA §10.6 non-linearizable quorum read. A writer connected to Athens sets the key, then updates it while replication from Athens to the other replicas is delayed. Alice, reading through Byzantium, is forced onto a quorum that includes Athens and sees the new value; Bob, reading later through a quorum of Byzantium and Cyrene, still sees the old value.

The small surface area gives the LLM very little room to hallucinate, and a reviewer reads the output as an experiment description rather than auditable code. If the LLM errs, the internal DSL fails to compile, giving it an opportunity to self-correct.

Two Distinct LLM Roles

The examples reveal a pattern: the LLM is useful in two fundamentally different ways. In the first phase—designing the abstraction or DSL itself—the LLM is best treated as a brainstorming partner rather than a code generator. The design constraints, trade-offs, and edge cases of a semantic model cannot be specified upfront; they emerge by building. This phase is inherently iterative: propose a structure, try it against a real case, observe where it is awkward, and feed that back into the next round. The LLM accelerates the loop by sketching alternatives, critiquing a design, or porting ideas across languages, but the human stays firmly in charge, because these design decisions must be fully owned. The structures that make a DSL pleasant—progressive interfaces that reject illegal scenarios at compile time, or a semantic model kept separate from the builder—emerge from iteration, not from a specification.

The second phase begins once the abstraction is stable. The LLM becomes a natural-language interface to what has been built. Prompts like “implement a quorum store as a Tickloom Replica” or “write a scenario reproducing the DDIA §10.6 read” work because the English description maps directly onto a pre-defined vocabulary. The abstraction grounds the prompt and verifies the result, which is what makes the LLM dependable.

The Generated Program, Not the Prompt, Is the Artifact

There is a trend toward treating prompts as the primary source of truth. A well-designed DSL flips that dynamic. The generated program often becomes the artifact that humans actually maintain. Because a DSL is dense, expressive, and free of boilerplate, it captures the essential intent in a form that remains readable long after generation. If a Tickloom failure scenario needs to change months later, there is no need to recover an original prompt and regenerate everything; the DSL carries enough context for an LLM—or a human—to understand and modify the scenario directly. The prompt was transient; the semantic model and DSL are the durable assets.