Tooling · Developer Experience
From Docs to
Runnable Code in One Command
A reference implementation in a doc proves a pattern reads well. It doesn't prove anyone can actually reproduce it under deadline. The real test is whether a brand-new domain, generated from nothing but a name, passes every automated check the very first time.
This repo's docs/reference.md defines a practical implementation template — a small worked example (historically, an Order domain) showing every layer, every file, every naming convention in one place. A written template is useful right up until someone has to actually type it all out correctly for the fifth new domain in a row. The next step was turning that template into a generator: a script that takes just a domain name and produces real, harness-passing code.
What Gets Generated, in One Pass
Running the Go generator against a brand-new domain name produces, in one shot, an Aggregate with a single state field cycling through PENDING/ACTIVE/CANCELLED, CQRS Command and Query Handlers, one Domain Event, a Repository (domain interface plus infrastructure implementation), an HTTP Handler and DTOs, and a migration:
# Default: generates under examples/internal/..., doesn't touch main.go/router.go,
# just prints to the console the content you should paste in
go run . Coupon
# With --wire, it also auto-inserts into cmd/server/main.go (repository assembly + registration
# in the shared outbox handler map) and internal/interface/http/router.go (Handler assembly +
# route registration)
go run . Coupon --wire
# To generate into a different project (e.g. one cloned from this repo as a template), specify --out
go run . Coupon --out /path/to/other-project --wireWhat comes out is deliberately a skeleton, not a finished feature — an empty CRUD-style starting point. The actual business rules, error messages, and domain-specific fields still need to be filled in by hand. What the generator buys isn't "you never write domain logic again" — it's "you never have to remember, by hand, all thirty-some small conventions (file naming, layer placement, Repository method names, the Outbox registration call) that a from-scratch domain needs to pass the harness on day one."
The Verification That Actually Matters
A generator that produces plausible-looking code isn't the same as a generator that produces code passing every rule the harness checks. Confirming that gap is closed means generating a domain nobody's ever used before — one entirely unrelated to the existing example domains — and running the harness against it for real:
go run . Coupon --wire
bash harness.sh <projectRoot>
# → A (100/100)This was tested against multiple-word and irregular-plural domain names specifically because that's where a naive code generator tends to break first — a pluralization rule based on simple suffix rules (+s, +es, y→ies) handles Coupon → coupons fine but needs manual touch-up for something like a domain whose plural doesn't follow that pattern. Confirming the generator scores 100/100 against domains it was never specifically tuned for is what actually validates that the docs and the tool agree — not a single successful run against the one example the generator's author had in mind while writing it.
The Recurring Bug Class: The Generator Falls Behind the Rules It's Supposed to Satisfy
The single most common failure mode across every language's generator, discovered repeatedly across unrelated feature rounds: a new harness rule gets added — a naming convention, a request-context pattern, an Outbox structural change — the manual example code gets updated to comply, and the generator quietly keeps emitting the old pattern, because nobody re-ran it after the rule changed.
This happened concretely more than once. When a Repository method-naming rule was introduced (unifying scattered patterns into find<Noun>s/save<Noun>/delete<Noun>), the Go and kotlin-springboot generators were both found — during an unrelated 5-language benchmark run, not a dedicated audit — to still be emitting the old find-by/bare-save shape, because neither had been touched since that rule shipped; both were rewritten to the same Find<Domain>/FindOne + Save<Domain> convention the real domains already used. When NestJS's request-scoped user-context store (@Authenticated() + UserContextStore.getRequesterId()) replaced direct req.user access, the NestJS generator's Controller template kept emitting the old req.user pattern and failed the harness the moment a fresh domain was scaffolded — a separate fix from the one applied to the hand-written example, because the two aren't the same artifact. The same thing happened again when the Outbox pattern moved from a single-pass drain to a multi-pass one across all five languages: every generator needed the identical structural fix as the hand-written example, as a second, distinct commit.
A generator is itself a second implementation of every convention it emits, maintained separately from the code it's copying the shape of. Any process that updates a rule and the example without also asking "does the generator still produce this?" will drift, reliably, every single time — not occasionally.
A Bug the Generator Itself Introduced
Generators aren't just at risk of falling behind rules — they can also carry their own independent defects that the manual example never had, because the templating logic is a separate piece of code with its own bugs. One generator's scaffolded "cancel" handler was found to be missing a mapping for an already-cancelled state — a real gap that would have silently produced a generic 500 error instead of the correct 400, for every single domain generated with that tool until it was found and fixed. That's not a documentation drift issue at all; it's a bug in the code that writes code, and it only surfaces by actually generating something and exercising the unhappy path, not by reading the generator's source.
Why This Is Worth the Maintenance Cost
Every language in this repo ended up with its own version of this tool, each idiomatic to that ecosystem — a Node script for NestJS, a standalone Go module using go run . since Go has no natural place to hang scaffolding scripts off an existing module, a Python script for the two Spring Boot ports (deliberately Python rather than requiring the Java/Gradle toolchain to boot just to scaffold a file), and one for FastAPI. All five follow the identical contract: take a domain name, optionally a --wire flag to auto-register the new domain instead of just printing the snippet to paste in, and an --out flag to target a different project entirely — useful for treating this repo as a template to bootstrap a brand-new service from, not just as a reference to copy by hand.
The generator earns its keep twice over: once as a genuine productivity tool for scaffolding a real new domain, and once as a running regression test for the docs themselves — every time it's re-run against a name nobody's used before and re-verified against the harness, it's really asking "do the documented conventions and the tool that's supposed to embody them still agree with each other." That question turned out to need re-asking more often than expected.
docs/reference.md — the reference implementation template every generator is built from · implementations/go/scripts/create-domain — the Go generator's real source