Show HN: Commune, one feed for newsletters on Beehiiv, Kit, Ghost, and more ZenWave Domain Language creator Ivan Garcia Sainz-Aja argues that reading the volume of Markdown generated by LLMs is the main bottleneck in spec-driven development, since agents treat unread decisions as approved. Garcia Sainz-Aja points to his domain-specific language, ZDL, which describes backend services concisely and from which a sample Order Fulfillment service generates a 291-line OpenAPI spec and a 302-line AsyncAPI spec. He says LLMs have been able to read and understand ZDL without prior training for at least a couple of years. Spec-Driven Development: Reading Markdown is the bottleneck I believe that reading Markdown at the speed and amount that LLMs are able to generate is impossible and will be the main bottleneck in designing systems in the agentic era. A question for those of you that use spec-driven development SDD to build software: how do you manage to read all the Markdown files that your LLM generates while it creates the specifications? I find it exhausting. Most of the time I find it impossible. And when I don’t read everything, decisions slip into the implementation as if I had already approved them. And I only find out when testing that the decision was already marked as approved in some Markdown file somewhere, and the agent took it as gospel :-O And yes, AI interviews like Matt Pocock’s Grill with Docs can help a lot with disambiguating and creating a shared understanding, but I still find many functional and architectural decisions slip in. And it’s becoming nearly impossible to keep up with LLMs’ verbosity. You can’t read at the speed an LLM writes I believe that reading Markdown at the speed and amount that LLMs are able to generate is impossible and will be the main bottleneck in designing systems in the agentic era. A language for decisions, not for prose 4 years ago I created a domain-specific language for describing microservices. I created this DSL to let me think about software and talk about software and then generate all the boring parts that were already present in the DSL. Currently this code generator, if it matches your technical stack, is still more effective and efficient than asking an LLM to implement what this DSL describes. But models improve, and I don’t expect it to stay more effective and efficient forever. Even without the SDK, ZenWave Domain Language is still a better way to describe and validate backend services than Markdown prose. It is concise, it’s developer-friendly, it reads as a compact programming language, and it holds all your domain’s Ubiquitous Language along with all the structure of the Domain-Driven Design building blocks: aggregates, entities, relationships, services, commands and domain events. It even connects to your APIs like OpenAPI and AsyncAPI using decorators. I cannot even remember the last time I wrote an API in YAML instead of using ZDL Domain Language as a concise IDL. The Order entity is defined in the same file; this is just the service and events part. / Order Application Service / @rest "/orders" service OrderService for Order { / Customer places an order / @post @transition to: PLACED placeOrder PlaceOrderInput Order withEvents OrderPlaced / Set Order as paid / @post "/{orderNumber}/pay" @transition from: PLACED, to: PAID payOrder @natural id, PayOrderInput Order withEvents OrderPaid / Set Order as shipped / @post "/{orderNumber}/ship" @transition from: PAID, to: SHIPPED shipOrder @natural id, ShipOrderInput Order withEvents OrderShipped / Set Order as cancelled / @post "/{orderNumber}/cancel" @transition from: PLACED, PAID , to: CANCELLED cancelOrder @natural id Order withEvents OrderCancelled / Query order / @get "/{orderNumber}" getOrder @natural id Order? } / Domain Events / @copy Order @asyncapi { channel: "OrdersPlacedChannel", topic: "orders.placed" } event OrderPlaced { id Long version Integer } @copy Order @asyncapi { channel: "OrdersPaidChannel", topic: "orders.paid" } event OrderPaid { id Long version Integer } // OrderShipped and OrderCancelled omitted From the above DSL you can derive two full APIs: Order Fulfillment OpenAPI: 291 lines of yaml Order Fulfillment AsyncAPI: with 302 lines of yaml And it’s not only for humans And it’s not only for human experts. For at least a couple of years now, LLMs have been able to read and understand ZDL without any previous training on my side, just because it is public and moreover because it says what it represents… plainly. And because it has a very specific grammar and some examples, they can also write it and reason about it. If LLMs are good at something, it is at holding lots of context in memory and cross-understanding what we humans can only get by going back and forth around docs and source code. When I was almost about to discontinue my efforts on ZenWave SDK and its first DSL, the ZenWave Domain Language, I came to realize that it’s the best tool of the whole toolkit. One bounded context at a time is not enough But designing or understanding one bounded context at a time is not enough in this day and age of Agentic Engineering and the big design upfront required to take full advantage of agentic loops and Spec-Driven Development. We need a new companion language that can join together several bounded contexts in one or many business workflows. So stay tuned for the next article, because we are going to bring some structured output to process-level Event Storming workshops. Originally published at ivangsa.com.