# Domain Specific Languages (DSLs)
A Domain Specific Language is a language built to express one narrow thing well, instead of everything adequately. SQL for queries. Regular expressions for pattern matching. CSS for styling. [[Mermaid]] for diagrams. Dockerfiles, Makefiles, Kubernetes manifests, Gherkin scenarios.
The trade is deliberate: you give up generality and you get expressiveness, safety, and readability inside one domain. A general-purpose language ([[Python]], [[Rust]], Java) can express anything, which is exactly why it cannot stop you expressing something wrong.
## The two families
Martin Fowler's split is the one everyone uses, and it is still the most useful:
**External DSLs** have their own syntax and their own parser. SQL, regex, CSS, Gherkin. You control the grammar completely, error messages can speak the domain's language, and non-programmers can often read them. The cost is that you now own a parser, an editor plugin, a formatter, and a debugger, or you own the absence of all four.
**Internal DSLs** (also called embedded) are written inside a host language, using its syntax bent into a domain shape. Ruby's RSpec, Kotlin's Gradle scripts, [[Rust]] macro-based builders, method-chaining fluent interfaces. You inherit the host's tooling for free, which is a very large free. You also inherit its syntax, so the language reads like the domain only up to whatever the host permits.
A third option deserves naming because it is the most common in practice: **configuration languages**. YAML, TOML, and JSON schemas used as a DSL. No parser to write, universal tooling, and a ceiling you hit the moment you need conditionals. Every large YAML system eventually grows a templating layer, and that layer is always worse than a real language would have been.
## Why build one
- **The domain has vocabulary you keep re-encoding.** If your code repeatedly translates the same domain concepts into loops and conditionals, that translation is the language asking to exist
- **Non-programmers need to read or write the rules.** Business rules, test scenarios, and configuration are the classic cases. Gherkin exists entirely for this
- **You want whole categories of mistakes to be unrepresentable.** This is the strongest reason. A well-built DSL does not catch errors, it makes them impossible to write
- **The same intent gets expressed twenty different ways.** Constraining that variation is worth real money in review time
- **Agents need to produce it.** New, and increasingly the deciding argument. See [[DSLs Make LLM Output Reliable]]
## Why not to
The honest failure modes, because DSLs fail more often than they succeed:
- **The tooling gap.** Your language has no autocomplete, no debugger, no formatter, no syntax highlighting, and no Stack Overflow. Developers feel every one of those absences daily
- **The generality creep.** It starts constrained. Then it needs a conditional. Then a loop. Then variables, then functions, then a module system. Now you have a general-purpose language with a bad type system and one maintainer. This is the single most common way DSLs die
- **The maintenance owner.** A language needs someone who owns its semantics. When that person leaves, you have a legacy compiler nobody understands and a codebase written in it
- **The learning cost is real and recurring.** Every new hire learns it, and unlike Python, that knowledge transfers nowhere
- **The "non-programmers will write it" promise usually fails.** They read it happily. Writing it turns out to be programming, which was the hard part all along
The rule of thumb: build a DSL when the domain is stable, the constraint is the point, and you can commit to owning it. Reach for a library with a well-designed API first. Most desires for a DSL are actually desires for a better interface, and a good API delivers most of the benefit at a fraction of the cost.
## Semantic model
The part most DSL efforts skip, and the reason they end up brittle. Fowler's insistence: **the DSL is not the point, the semantic model is.** Build the object model that represents the domain first, make it work and testable on its own, and only then put a language in front of it as one possible interface.
Do it this way and the DSL becomes a thin parsing layer over something you can test directly. Skip it and the parser accumulates logic, which means your business rules now live inside a grammar. That is where the bad ones end up.
## Why they matter more now
For most of their history DSLs were an internal-quality argument: readability, safety, reduced duplication. Real benefits, hard to justify against the tooling cost, which is why most teams correctly said no.
Language models change the arithmetic. A constrained language with a validator is the most reliable interface you can give a model, because the search space is small enough that a short spec teaches it and a parser can reject anything wrong. The tooling cost is unchanged; the payoff column got a new, large entry. That does not make DSLs a default. It does mean a decision you correctly declined three years ago is worth re-examining.
## References
**Books**
- Martin Fowler, *Domain-Specific Languages* (Addison-Wesley, 2010). The reference work. Pattern catalogue, internal vs external, semantic model, parser construction
- Debasish Ghosh, *DSLs in Action* (Manning, 2010). Internal DSLs in JVM languages, strong on functional approaches
- Terence Parr, *Language Implementation Patterns* (Pragmatic Bookshelf, 2009). The practical how-to for external DSLs, from the author of ANTLR
- Markus Völter, *DSL Engineering* (2013, free PDF at http://dslbook.org). Language workbenches and model-driven development
- Eric Evans, *Domain-Driven Design* (Addison-Wesley, 2003). Ubiquitous language, the idea DSLs operationalize
**Articles**
- Martin Fowler's DSL guide — https://martinfowler.com/dsl.html
- Martin Fowler, "DomainSpecificLanguage" — https://martinfowler.com/bliki/DomainSpecificLanguage.html
- Unmesh Joshi, "DSLs Enable Reliable Use of LLMs" — https://martinfowler.com/articles/llm-and-dsls.html
## Related
- [[DSLs Make LLM Output Reliable]]
- [[Martin Fowler]]
- [[Mermaid]]
- [[Rust]]
- [[Python]]
- [[JavaScript Object Notation (JSON)]]
- [[Large Language Models (LLMs)]]
- [[Context Engineering]]
- [[Software Architecture]]