When I was building an AI to control an astronomical telescope, I seriously considered one approach: designing a small language that only allowed a limited set of operations — taking photos, focusing, pointing at celestial objects. That is a DSL, a programming language designed for a specific domain. It can confine the AI’s actions within a safe boundary. The problem was the cost. Designing the language, writing an interpreter, then adding loops and branches — before you know it, you’re reinventing a general-purpose programming language. So I abandoned that path in the article I wrote at the time and ended up choosing Python, a restricted set of libraries, AST analysis, and sandboxing.
Since then, AI has shifted that calculus. I wrote in “Disposable Software and Compressed Reality”: when the cost of building specialized tools drops significantly, it can become more economical to write a tool on the spot for a tiny, even one-off need, rather than making do with rough approximations. This judgment applies equally to DSLs. AI can not only use an existing language, but also help people experiment with abstractions, write parsers, add validators, and prepare examples.
On July 14, 2026, distributed systems engineer Unmesh Joshi published
DSLs
Enable Reliable Use of LLMs. He proposes a two-phase approach:
first, let humans and LLMs discover domain concepts through real
implementations, crystallizing the stable parts into a DSL; once the
language stabilizes, let the LLM translate natural language requirements
into DSL programs, to be executed by compilers, simulators, and tests.
The generated programs then enter the codebase, becoming the
source of truth for subsequent modifications.
This article gave me a new way to understand the value of DSLs. It is not just a new syntax, but a way to build a smaller executable world for LLMs. What was once hardest to bear was the cost of constructing that world; now, that cost too is falling.
Natural language gives LLMs an expressive space with almost no boundaries. The same requirement can be phrased in endless ways, and omitted premises are nearly impossible to exhaustively enumerate. When a model issues instructions to a system directly in natural language, the system may not even know how to parse the results.
Python and JavaScript eliminate syntactic ambiguity. Code can run, can be reproduced, and can be tested. But general-purpose programming languages still leave the model with a vast number of choices: it can call the filesystem, create threads, open network connections, and it can implement the same delay in dozens of ways. The syntax becomes precise, but the external effects the model can trigger remain numerous.
Domain APIs and DSLs shrink it by another layer. The system only exposes a limited set of actions — taking photos, focusing, starting tasks, querying status — and specifies which combinations are legal. The model no longer faces the entire operating system; it only needs to choose among actions the team already understands. What I mean by “executable semantic world” here is: what actions the model can express, how those actions can be composed, and what rules the system will check.
Once the world is shrunk, the system can eliminate some invalid paths before execution. Checking ten domain actions and their combinations is also far easier than auditing arbitrarily generated Python programs. That said, shrinking the world only eliminates errors already covered by rules; correct answers do not magically emerge from this alone.
FastAPI is a server-side framework that exposes Python operations through HTTP interfaces. A more familiar example is the GitHub REST API: an agent can create issues, read pull requests, or trigger workflows through fixed endpoints, without needing to understand how GitHub’s backend persists objects and schedules tasks. Domain services written with FastAPI do the same thing — hiding internal implementation behind a set of stable operations.
This is already shrinking the agent’s world. The API tells the model
what atomic actions are available, and can constrain each action through
authentication, authorization, and parameter validation. However, a
smaller control surface does not guarantee that the model’s ultimate
impact will be small. The GitHub API can trigger a GitHub
Actions workflow, and the run step inside a workflow
can still execute shell commands, read and write files, or access the
network. Judging whether the boundary is narrow enough ultimately
depends on what external effects the actions can trigger.
APIs have another characteristic: the composition logic typically lives inside the agent. The model creates a pull request, checks the CI status, then decides whether to merge. The complete plan exists only in the execution trace, not necessarily as an independent file. After the first three steps have already altered the system, if the fourth step fails, rollback becomes messy.
GitHub Actions workflows take another step toward DSLs. A YAML file does not merely list individual actions; it also captures trigger conditions, jobs, dependencies, and complete steps. The system can parse it before side effects occur, check certain cross-step rules, hand it off for human review, and store it in Git for future diffing or replaying. The relationship between the two can be understood through an informal but useful formula:
DSL ≈ domain API + composition rules + storable program representation
If a FastAPI endpoint accepts an entire JSON plan containing steps, dependencies, and error handling, that request body is effectively playing the role of a DSL program, with the server responsible for interpreting and executing it. Conversely, a DSL often ends up compiling into a sequence of API calls. There is no clear adversarial line between the two.
Interactive tasks are well-suited to direct API calls. The model submits a change, reads the CI error, then decides the next step — observation and action alternate continuously. Writing a complete plan upfront could actually strip away its ability to adjust based on live feedback.
Another class of tasks demands seeing the full picture first. Deployment plans, permission changes, and distributed failure scenarios often involve multiple steps and cross-step constraints. The system is better off knowing what will be changed later and how failures will be handled — before the first step even executes. At this layer, a DSL or structured JSON plan preserves the complete intent; parsers and validators check it first, and once it passes, execution is handed off to FastAPI, MCP, or the CLI.
Thus, FastAPI can handle atomic operations, while the DSL describes the complete plan. The former addresses “how to execute concretely”; the latter addresses “how these operations compose into a domain task.” The system does not need to choose one over the other.
Before adopting a DSL, traditional teams typically have to answer one question first: will this language be used repeatedly in the future? Parsers, validators, examples, and migration tools all require human maintenance; without sufficient usage volume, the upfront investment is hard to justify.
AI lowers the production cost of these artifacts and changes the decision sequence. A team can first establish domain boundaries for a few similar tasks at hand, then decide its lifespan after actually using it. This does not demand that the DSL be throwaway, nor that it be permanent infrastructure from day one.
If it repeatedly solves similar problems, it can stay in the codebase and gradually mature into a stable component; if only a few primitives prove useful, extract them; if the corresponding tasks disappear, archiving or deleting it is fine. The lifecycle shifts from a prediction made before creation to an observation made after use.
Keeping the code itself is cheap; the real cost is maintenance and context pollution. An experimental DSL can reside in the archive without being loaded by default for every task. Only the stable, recurring parts should enter active documentation, examples, and tool descriptions. This preserves the possibility of future reuse while preventing the model from facing a pile of stale interfaces each time.
Once a project uses a DSL, it accumulates three types of material: programs that ran successfully, validator errors from failures, and human corrections to erroneous results. When the next task arrives, these materials are more useful than an abstract syntax reference. The model can see how this project has expressed similar tasks in the past, which combinations have failed, and what the team later changed.
If the system continuously captures these records, distills stable patterns from them, and loads relevant portions as needed for each task, they enter what I previously discussed as Context Infrastructure. This infrastructure does not merely store chat logs; it organizes the project’s own judgments, examples, and corrections into context that the model can continue to use.
Context Infrastructure and DSLs exert different kinds of constraint. The former tells the model what matters and how judgments were made in the past; the latter directly stipulates what actions can be invoked and how they can be composed. One shapes how the model understands the task; the other limits what programs the model can ultimately submit.
Each execution generates new candidate material. Successful programs can become subsequent examples; failures may stem from the model writing incorrect programs, or they may expose problems in the language, the validator, or the task itself. Humans and the system first classify and refine these records, then update the Context once stable patterns are confirmed. The model consumes context from the codebase to complete tasks, and the filtered results return to the codebase, driving this local language to continue evolving.
If the domain boundary is too wide, the model still has many useless
paths; if it is too narrow, new requirements may be inexpressible
altogether. The key judgment here is not whether the DSL is
Turing-complete, but two more concrete questions: what external effects
the model can ultimately trigger — that is, the
effect surface — and what combinations of those actions the
system allows.
Even when a DSL program passes through the parser and compiler, that only proves it conforms to the rules that have been written down. Business outcomes still require independent testing. Otherwise, the same model might simultaneously misunderstand the task, generate the program, and write expectations consistent with the misunderstanding — ultimately producing a result that passes cleanly but points in the wrong direction.
Tickloom demonstrates how these layers can be combined. It uses a Java internal DSL to describe servers, clients, reads, writes, and network failures, then uses a deterministic simulator, assertions, and a consistency checker to verify scenario behavior. The DSL constrains expression, the simulator handles execution, and tests judge the results for assertions already in place.
That said, Tickloom is an engineering case study, not a systematic experiment on DSL reliability. The project does not compare generation success rates for the same set of tasks under natural language, JSON, plain Java, and DSL, nor does it have cross-model benchmarks. It can illustrate how the mechanism works; it cannot prove that DSLs have generally improved LLM task accuracy.
Early-stage LLM projects often pour the bulk of their effort into prompts: add a rule, include another example, remind the model not to repeat last time’s mistake. As a project matures, the truly stable assets may shift elsewhere: domain objects and actions, restricted APIs, DSLs, validators, successful programs, and the corrections left behind after each failure.
FastAPI defines the atomic capabilities the model can invoke, the DSL composes these capabilities into a complete plan, and Context Infrastructure preserves why the project was designed this way and what was learned from past use. Together, they bring a general-purpose model into the project’s own executable world.
Joshi’s article discusses how LLMs can work with DSLs. Taking the cost shift one step further, it also hints at another possibility: AI can not only work within a constrained world, but can also lower the cost of building and modifying that world. Teams can establish dedicated boundaries earlier, put them in the codebase, and let real usage determine which parts persist.
This path still has clear prerequisites. Domain actions need to be relatively stable, the system must be able to check critical outcomes, and the boundary must not be so narrow that it excludes legitimate requirements. When these conditions are met, writing ever-longer instructions for the model may not be the most effective investment. Building it a smaller, executable, experience-accumulating world may come closer to a reliable engineering system.