DSLs Enable Reliable Use of LLMs

ingve3 pts0 comments

DSLs Enable Reliable Use of LLMs

DSLs Enable Reliable Use of LLMs

LLMs generate code incredibly fast, but to ensure they generate<br>exactly what is intended, they need clear boundaries. Abstractions and<br>Domain-Specific Languages (DSLs) provide a strong harness that guides LLMs<br>right from the start. The example of Tickloom - a domain model and DSL for<br>illustrating distributed system behavior - shows how we can use an LLM as a<br>partner to iteratively build a DSL and as a natural language interface to use<br>it. Such a DSL can act as the key source of truth for software systems in the<br>world of LLMs.

14 July 2026

Unmesh Joshi

Unmesh is a Distinguished Engineer at Thoughtworks, based in Pune, India. He is the author<br>of Patterns of Distributed Systems.

Contents

The Limits of Upfront Specification

Design Is Discovered Through Implementation

Domain Abstractions and DSLs

Why DSLs work so well with LLMs

Example: Using LLMs to generate diagram rich powerpoint presentations

Building the Semantic Model

Example: Tickloom — a semantic model for distributed systems

Even good abstractions help — without a DSL

Example: Building a DSL for testing distributed system scenarios

Two phases working with LLMs

The DSL as the Source of Truth

Modern LLMs possess an incredible capability. They can generate large amounts of code, and<br>sometimes entire systems, from just a high-level natural language description. An important<br>assumption here is that the 'intent' of what needs to be built is well articulated, using<br>precise words that LLMs can map to coding building blocks.<br>However, there are two important points worth noting: the limits of<br>upfront specification, and how design is discovered through implementation.

The Limits of Upfront Specification

Building large systems involves a great many small design decisions, and these cannot all<br>be known in advance or driven entirely from a high-level spec. A specification is at best a<br>starting hypothesis: the real constraints, trade-offs, and edge cases are discovered<br>iteratively, as we proceed with the implementation. We discussed this at length in an<br>earlier article, where we called it Upfront Specification Impossibility.<br>The point is not that specs are worthless, but that the first one is a hypothesis to be<br>revised, never a finished blueprint.

The natural response is to iterate: refine the spec, generate code, review what comes<br>back,<br>and feed what we learn into the next round. That loop works well when each round produces a<br>small, reviewable change.

Design Is Discovered Through Implementation

Reviewing code, particularly while we are still discovering the design,<br>is not the same as writing it.<br>While reviewing the generated code, we review through the chunks validating<br>if it maps to our intent and looking for possible pitfalls.<br>But reviewing rarely forces us to wrestle with the design decisions.<br>Writing code, by contrast, forces us to think through concrete decisions—such as where a responsibility belongs<br>or what boundaries should be exposed so the design can be extended further.<br>It is in making those decisions that a design most fully reveals itself.

What Is Code?

Code has two distinct but intertwined purposes. It is a set of<br>instructions for a machine, and it is also a conceptual model of the problem<br>domain. A well designed codebase is a representation of the vocabulary of a<br>domain. These abstractions reveal themselves only as developers build the<br>software. Programming languages act as thinking tools, enabling the<br>construction of a conceptual model that supports later evolution. With LLMs,<br>code acts as essential context: good abstractions, executable behavior, tests,<br>types, and invariants all help constrain the model and make its output more<br>useful.

by Unmesh Joshi

12 May 2026

Read more…

article

generative AI

The programming language and<br>paradigm we code in shapes the design insight we get. A functional design approach or an<br>object-oriented design approach reveals different aspects of the design, along with idioms<br>and patterns that are natural to the paradigm.

So where do LLMs fit in?<br>I see LLMs playing two roles.<br>They are a great help while we shape the design and its vocabulary, acting as brainstorming<br>partners to help us explore the design space and discover the right abstractions.<br>Once the vocabulary is established, LLMs<br>work as an excellent natural language interface to it.

Domain Abstractions and DSLs

A useful way to frame this is through Domain Driven Design. Its<br>core insight is building a shared conceptual model of the domain in code, and then using that<br>model - which DDD calls a Ubiquitous Language - both to evolve<br>the codebase and to give the team a vocabulary to think and communicate in. Often, it is<br>highly effective to build a domain specific language on top of that model: a constrained syntax for<br>expressing the domain's concepts and operations. Seen this way, most development is the<br>process of building a domain model and using it to evolve the...

llms design code domain model dsls

Related Articles