Skip to main content

Clean Architecture, Vertical Slice, and Feature Modules: What Actually Helps in Real Projects

• By Yuriy Mykhalchuk • 6 min read
Featured image for Clean Architecture, Vertical Slice, and Feature Modules: What Actually Helps in Real Projects
Image credit: Generated technical illustration

There’s a stage almost every software product hits, where the team starts hunting for the architecture that will finally make the codebase behave itself.

By then the product isn’t small anymore. A few early decisions have hardened into habit, and a few probably shouldn’t have. Features start stepping on each other. The same business rule shows up in three different places. A change that sounded trivial in planning turns into a half-day trek through handlers, services, controllers, and database tables that all somehow know more than they should.

That’s usually when architecture stops being theoretical and starts being personal.

The trouble is, teams often go shopping for an answer in the wrong aisle. People often compare things like Clean Architecture, Vertical Slice, feature modules as three competing products on the same shelf, when really they solve different problems. Clean Architecture is mostly a set of instincts about dependencies. Vertical Slice is mostly about organizing behavior around change. Feature modules are mostly about ownership — who’s actually responsible for what, and where the boundaries sit. Mixing those categories up is where a lot of architecture debates go sideways before a single line of code changes.

I have a soft spot for Clean Architecture, honestly, and I think a lot of us landed there for good reasons. If a system has been shaped too much by frameworks, or database-first thinking, or whatever a controller found convenient at the time, Clean Architecture can feel like someone finally cracked a window open. The domain shouldn’t be reduced to whatever the web framework prefers. Infrastructure should serve the product model, not quietly define it. Both healthy instincts, worth keeping.

What’s less healthy is how often Clean Architecture turns into ceremony. A simple change ends up walking through layers that exist mainly because a diagram said respectable software has layers. Small use cases pick up interfaces and wrappers and indirections faster than they pick up actual clarity. The codebase gets tidier in shape and heavier to move through at the same time — which is a strange trade to make without noticing.

Vertical Slice often feels more honest about this. Instead of starting from layers, it starts from behavior — a feature, a command, a workflow lives close to the code that actually does the work. Reading a change gets easier, because the path is shorter and nobody has to reconstruct the whole application from five technical folders before touching anything. That’s especially valuable in products still figuring out what they are, where workflows shift constantly and every extra hop through the codebase is a small tax on changing your mind.

It’s not a complete answer, though. Without real boundaries, vertical slices just become a tidy arrangement of locally understandable mess. A handler can still reach through too many layers. One feature can still know far too much about another feature’s storage. The folder structure improves while the actual coupling quietly stays exactly where it was.

Which is why I think feature modules deserve more credit than they usually get. Probably because they’re the least fashionable of the three. A module doesn’t sound like a philosophy. It sounds like a decision someone will have to defend in code review. But that’s exactly the point. A module that’s doing real work isn’t just a folder called Features; it’s a boundary around an actual business capability. Who owns this part of the workflow? What data belongs here, and what’s meant to stay private even when exposing it would be convenient this week? Through what surface is the rest of the app allowed to reach in?

Take a document-heavy SaaS product — it’s easy enough to list the nouns early on: users, documents, evidence, reviews, controls, frameworks, notifications, billing. Deciding which of those actually own their rules, and which should only ever talk to each other through a proper boundary, is the hard part. Naming the pieces is easy, but keeping them from casually rearranging each other’s furniture is not. And that problem doesn’t go away just because you called the project Clean Architecture or Vertical Slice.

Which is also why I wouldn’t frame this as a choice between Clean Architecture and Vertical Slice in the first place. In real products the more useful question is what you borrow from each. The dependency instincts from Clean Architecture are genuinely worth keeping — infrastructure shouldn’t dictate the center of the system, business logic shouldn’t end up scattered as residue across controllers and ORM mappings, and the domain should get to protect its own decisions. The workflow orientation from Vertical Slice is worth keeping too — a change should be easy to follow, a new engineer should have a fair shot at finding the right place to make it without opening fifteen files that exist purely to preserve some pattern’s self-esteem. Feature modules are usually what makes those two ideas hold together once real pressure hits — they give the team a place to say this capability is real, it owns something, and you don’t get to treat it as public property just because the database happens to be nearby.

If I had to state a preference, it’d sound less tidy than most conference talks: feature or module boundaries as the primary organizing idea, vertical slices for behavior inside those boundaries, and Clean Architecture’s dependency instincts wrapped around the whole thing — especially wherever infrastructure starts quietly turning itself into the product by accident. Not a pure pattern by any definition. Fine by me. Purity was never really the point.

What matters more is whether the product stays changeable after a year of actual usage — real deadlines, partial rewrites, support fires, people taking understandable shortcuts under pressure. Did the structure make workflows genuinely cheaper to change, or just more respectable to draw on a whiteboard? Is ownership actually clearer, or are the modules just renamed folders sharing a table behind the scenes? Can someone follow a production issue without guessing? Did coupling really go down, or did it just move somewhere less visible? Those questions tell me more than whether the codebase matches a diagram from a book.

My main SaaS product so far, AlturaQuantera, landed on Clean Architecture, for what it’s worth — not as some ceremonial loyalty to the pattern, but as a fairly practical outcome of where the product started and which boundaries felt important early on. I still think it was the right call. The only thing I’d watch for is the usual trap: once a pattern starts feeling morally correct, it gets a lot easier to preserve its shape than to preserve its usefulness.

That’s probably the real risk hiding under most architecture debates. Teams rarely fail because they picked the wrong named pattern on day one. They get into trouble because the structure stops being questioned while the product keeps changing anyway — a once-helpful pattern quietly turns into habit, habit turns into ceremony, and eventually someone says the codebase is hard to change, and the proposed fix is another round of architecture.

Sometimes the actual fix is simpler: keep the boundaries real, keep the workflows readable, keep the dependencies honest. Borrow whatever reduces friction. Drop whatever mostly produces posture.

Most products don’t need architecture as identity. They need architecture that still helps on an ordinary Tuesday.