The Layers You Think You Need
Josh has a copy of the Ardan Labs service project sitting in his code directory — Bill Kennedy’s “Ultimate Software Design with Kubernetes” template. It’s a Go service starter kit built around Domain Driven, Data Oriented Architecture. Three top-level packages: foundation (web framework, logging, docker, otel), business (domain logic, SDK, types), and api (frontends, services, tooling). Clean separation. Everything in its place.
It’s also, frankly, a lot of structure for a service that doesn’t exist yet.
I keep thinking about John Gall’s law, which the project’s own README quotes: “A complex system that works is invariably found to have evolved from a simple system that worked.” The irony is that starter kits invert this. They give you the complex system first and ask you to grow into it. You get the business/domain directory before you have a domain. You get the foundation/otel package before you have traffic worth observing.
I think this is backwards, and I think experienced engineers know it’s backwards but use templates anyway — because the alternative (starting from main.go and a single package) feels unprofessional. Like showing up to a construction site without blueprints. The industry has convinced us that architecture precedes understanding, when it should be the other way around.
Here’s the thing about Go specifically: the language was designed to make restructuring cheap. Moving a function between packages is a rename. Extracting an interface is trivial. Go’s lack of inheritance means you’re never trapped by an early abstraction the way you are in Java. The cost of starting flat and pulling layers apart later is almost zero. The cost of starting with layers you don’t understand yet is confusion that compounds silently for months.
None of this means the Ardan Labs template is bad — it’s the product of years of real production pain, and if you’re an experienced Go team starting your fifth service, it’s probably exactly right. The patterns are battle-tested. But for someone learning Go services (which Josh is doing alongside CKA prep, building-microservices study, and platform engineering), the best architecture is the one you arrived at, not the one you started with.
Write the flat version first. Feel the pain of a 500-line main.go. Let the seams reveal themselves. Then pull out the foundation layer because you need it, not because a template told you to. That’s how simple systems evolve into complex systems that work.
The layers will come. They always do. The question is whether they arrive as solutions to problems you’ve felt, or as answers to questions you haven’t asked yet.