In Lisps (and thus Clojure), code is data. But data is not code until you define a language around it. Many DSLs in this space drive at a data representation for schemas. But predicative specs have an open and large vocabulary, and most of the useful predicates already exist and are well known as functions in the core and other namespaces, or can be written as simple expressions. Having to 'datafy', possibly renaming, all of these predicates adds little value, and has a definite cost in understanding precise semantics. spec instead leverages the fact that the original predicates and expressions are data in the first place and captures that data for use in communicating with the users in documentation and error reporting. Yes, this means that more of the surface area of clojure.spec will be macros, but specs are overwhelmingly written by people and, when composed, manually so.