After 20+ years in software, I've seen every approach to building systems. This is the one that actually works.
Every time I mention "doc-first" or "spec-driven development" to engineers, I get blank stares.
"That sounds great in a perfect world."
Here's the thing: We're software engineers. We create worlds. Why wouldn't we try to make them perfect?
This articleβand the complete codebase on GitHubβexists to demonstrate that spec-first isn't some ivory tower ideal. It's practical. It's powerful. And with the tools we have today, it's easier than ever.
π― What We're Building
Synapse is a complete event-driven order processing system built entirely spec-first:
- Specifications are the source of truth β OpenAPI 3.1 for REST, AsyncAPI 3.0 for events
- Code is generated from specs β Types, interfaces, clients, event handlers
- Conformance tests validate everything β Implementation must match the specification
- Real infrastructure via Testcontainers β NATS, PostgreSQL, Redis spin up for tests
The result? A system where the contract is king, drift is impossible, and tests prove conformanceβnot just behavior.
π The Doc-First Philosophy
Define Before You Build
Traditional development flows like this:
Write code β Document later (maybe) β Hope nothing drifts
Doc-first development flips the script:
Write spec β Generate code β Implement interfaces β Prove conformance
Generated > Handwritten
When code is generated from specs:
- β No drift between documentation and implementation
- β Type safety guaranteed by the spec
- β Clients auto-generated for any language
- β Changes flow spec β code, never backwards
Conformance Over Coverage
Traditional testing asks: "Does the code do what the code says?"
Conformance testing asks: "Does the code do what the **contract* says?"*
One tests implementation. The other tests promises.
π§ The Pipeline
Orders flow through three Watermill-powered stages:
| Stage | Purpose |
|---|---|
| Validate | Check required fields, verify amounts, validate customer |
| Enrich | Customer tier lookup, fraud scoring, inventory check |
| Route | Apply routing rules, determine destination, set priority |
Each stage publishes to NATS, persists to PostgreSQL, and caches in Redis. Failed events go to a Dead Letter Queue for retry.
π§ͺ Testing Strategy
OpenAPI Conformance
func TestOpenAPI_HealthEndpoint_ConformsToSpec(t *testing.T) {
// Start real infrastructure with Testcontainers
tc, _ := testutil.StartContainers(ctx, t, nil)
// Create test suite from OpenAPI spec
suite, _ := conformance.NewContractTestSuite(
"openapi/openapi.yaml",
)
// Validate response matches spec schema
result := suite.RunTest(ctx, client, baseURL,
"GET", "/health",
nil,
http.StatusOK,
"HealthResponse", // Must match this schema
)
assert.True(t, result.Passed)
}
AsyncAPI Conformance
func TestAsyncAPI_OrderPayload_ConformsToSpec(t *testing.T) {
// Create validator from AsyncAPI spec
suite, _ := conformance.NewEventContractTestSuite(
"asyncapi/asyncapi.yaml",
)
// Validate event payload against schema
result := suite.ValidateEvent(
"orders/ingest",
"OrderReceivedPayload",
orderJSON,
)
assert.True(t, result.Passed)
}
π Project Structure
synapse-spec-first/
βββ asyncapi/ # AsyncAPI 3.0 event specs
βββ openapi/ # OpenAPI 3.1 REST specs
βββ cmd/
β βββ synapse/ # Application entry point
β βββ synctl/ # Custom code generator
βββ internal/
β βββ generated/ # Generated from specs
β β βββ types.gen.go # 31 domain types
β β βββ server.gen.go # HTTP interface
β β βββ client.gen.go # HTTP client
β β βββ events.gen.go # Event handlers
β βββ handler/ # HTTP handlers
β βββ pipeline/ # Watermill pipeline
β βββ conformance/ # Contract testing
β βββ testutil/ # Testcontainers
βββ scripts/ # Diagram generation
π The Workflow
# 1. Edit the spec (the source of truth)
vim openapi/components/schemas/orders.yaml
# 2. Regenerate code
go run ./cmd/synctl
# 3. Implement the new interface methods
# (The compiler tells you what's missing)
# 4. Run conformance tests
go test ./internal/conformance/... -v
That's it. Spec changes β regenerate β implement β verify. The spec leads, the code follows.
π Standing on Shoulders
This project wouldn't be possible without brilliant work from:
Specification Standards
- OpenAPI Initiative β Giving REST APIs a language
- AsyncAPI Initiative β The same for event-driven systems
- JSON Schema β The foundation for validation
Testing Infrastructure
- Testcontainers β Real integration testing made accessible
- Watermill β Elegant Go event-driven development
The Go Ecosystem
π€ "But in the Real World..."
I've heard every objection:
"We don't have time to write specs first."
You don't have time to debug integration issues from undocumented API changes either. Pick your poison.
"Specs get out of date."
Not when they generate code. Not when conformance tests fail on drift.
"It's too much overhead."
The overhead is front-loaded. The payoff compounds forever.
π Try It Yourself
# Clone the repo
git clone https://github.com/copyleftdev/synapse-spec-first.git
cd synapse-spec-first
# One-time setup
make setup
# Run all tests (including conformance)
make test
# Start the server
make run
The Makefile Experience
We've included a comprehensive Makefile because developer experience matters:
make help # See all available commands
make generate # Regenerate code from specs
make test-conformance # Run contract tests
make test-pipeline # Run integration tests
make diagrams # Generate architecture diagrams
make dev # generate β test β run (full cycle)
| Workflow | Command |
|---|---|
| First time setup | make setup |
| After spec changes | make generate |
| Quick validation | make test-short |
| Full test suite | make test |
| CI pipeline | make ci |
copyleftdev
/
synapse-spec-first
Doc-First Event Processing: A demonstration of specification-driven development with Go, OpenAPI, AsyncAPI, and Testcontainers
Synapse: Doc-First Event Processing
"We're software engineers. We create worlds. Why wouldn't we try to make them perfect?"
A complete demonstration of specification-driven development using Go, showcasing how to build event-driven systems where the spec is the single source of truth.
What Is This?
This project demonstrates a doc-first development workflow:
- Write specifications first (OpenAPI 3.1 + AsyncAPI 3.0)
-
Generate code from specs (custom
synctlgenerator) - Implement to interfaces (handlers, pipeline stages)
- Validate with conformance tests (responses must match specs)
Quick Start
# Clone the repo
git clone https://github.com/copyleftdev/synapse-spec-first.git
cd synapse-spec-first
# One-time setup (downloads deps + generates code)
make setup
# Run all tests
make test
# Start the server
make run
Makefile Commands
This project includes a comprehensive Makefile for a pleasant developer experience:
make help # Show all available targets
π Quick Commands
Command
Description
make setup
One-time setup for new clones
make generate
Regenerate code
πͺ A Challenge
If you've never tried spec-first development, I challenge you:
Build your next API starting with OpenAPI.
- Write the spec first
- Generate your types
- Implement to the interface
- Write conformance tests
Then tell me it's not worth it.
π Final Thoughts
Someone once told me, "In a perfect world, that would be great."
We're software engineers. We create worlds.
Why wouldn't we try to make them perfect?
This project is a living demonstration. Fork it. Learn from it. Improve it. And maybe, just maybe, next time someone mentions doc-first development, there will be one fewer blank stare.
20 years of testing taught me this: Quality isn't an afterthought. It's the architecture.
Now go build something beautiful.
π Resources
- Full Codebase: github.com/copyleftdev/synapse-spec-first
- OpenAPI Spec: openapi/openapi.yaml
- AsyncAPI Spec: asyncapi/asyncapi.yaml
- Conformance Tests: internal/conformance/







Top comments (0)