Build small Clojure domain-specific languages from data, functions, and macros only where syntax is justified, while preserving validation, tests, expansion review, and maintainable error messages.
In Clojure, a domain-specific language often starts as data. You represent rules, pipelines, policies, or configuration as maps and vectors, then interpret those values with ordinary functions. Macros can help, but they are not the default design tool.
This chapter teaches DSL design that stays readable and testable, with clear boundaries between syntax, data shape, validation, and evaluation. It also shows how to avoid the common failure mode: a clever mini-language that only the author can debug.
For Java engineers, the theme is the same as elsewhere in Clojure: make intent explicit, keep the evaluation core pure, and push complexity to the edges where you can validate, explain, and test it.
| DSL design question | Clojure habit to practice |
|---|---|
| Can data express it? | Prefer EDN-shaped data plus an interpreter before inventing new syntax. |
| Does syntax matter? | Reach for macros only when call-site shape or evaluation control is the real requirement. |
| How will users fail? | Validate early and return errors that point to the DSL form or data path that caused the problem. |
| How will reviewers trust it? | Test the interpreter, inspect macro expansion, and document the smallest working vocabulary. |