BackendSeptember 20, 20265 min read

The Modular Monolith: Discipline Without the Network Tax

Swastika Dey Roy
Swastika Dey Roy
The Modular Monolith: Discipline Without the Network Tax

A modular monolith gives you the thing microservices were actually promising, which was clear boundaries between parts of your system, without the thing microservices quietly charge you for, which is running a distributed system. One deployable application, internally split into modules that talk through explicit interfaces. You keep the discipline. You skip the network.

That distinction matters because most teams that adopted microservices didn't need independent scaling or polyglot stacks. They needed their codebase to stop turning into spaghetti, and boundaries solve that problem on their own.

The network tax is real money and real latency

Every time you split a function call into an HTTP or gRPC call, you buy a bundle of new problems: serialisation, retries, timeouts, partial failures, distributed tracing, and a bill for the infrastructure that moves the bytes. Amazon's Prime Video team documented this vividly when they consolidated a distributed monitoring service back into a single process and cut infrastructure costs by 90% while handling more streams, not fewer. As The New Stack's analysis noted, the expensive part wasn't the logic. It was the orchestration and data transfer between components that used to be a function call away.

Inside a modular monolith, a call between the billing module and the invoicing module is still just a method call. It's measured in nanoseconds, it can't half fail, and it never shows up on your cloud invoice.

Boundaries are enforced by tooling, not goodwill

The classic objection is fair: without the hard wall of a network, module boundaries erode under deadline pressure until you have a big ball of mud again. The answer in 2026 is that boundaries are checked by machines, not by code review vigilance.

Shopify runs one of the largest Rails codebases anywhere, roughly 2.8 million lines of code organised into 37 components, and enforces the lines between them with Packwerk, a static analysis tool that fails the build when a component references another component's private code. Their engineering team wrote up why they chose modularisation over extraction after concluding that most of the pain in their monolith came from tangled dependencies, not from being a single application.

The JVM world has the same machinery. Spring Modulith lets you declare modules and their public interfaces, then verifies in your test suite that nothing crosses a boundary it shouldn't, including dependency cycles. In .NET, architecture tests do the same job. The point generalises: if a boundary violation breaks CI, the boundary holds.

You can still extract services later, from a position of knowledge

Martin Fowler's MonolithFirst argument has aged well: nearly every successful microservices story he saw started with a monolith that grew too big, while systems designed as microservices from day one tended to struggle, because you rarely know your true service boundaries until the system has lived a while. A modular monolith is the practical version of that advice. Modules that communicate through interfaces and events are already shaped like services. If one module genuinely needs independent scaling or its own release cadence, you extract that one module, with real production data telling you where the seam is.

This is where architecture stops being a technology decision and becomes a delivery decision. Getting the module boundaries to mirror your business domains is the hard part, and it's the kind of judgement call where an outside perspective on technical architecture and backend design pays for itself, because redrawing boundaries after launch costs ten times what drawing them well costs before it.

The radar above is illustrative rather than measured, but the shape tells the story: the modular monolith concedes independent scaling to microservices while keeping the deployment simplicity and cost profile of a single application. For most product teams, that's the right trade.

FAQ

Is a modular monolith just a monolith with folders?

No. Folders are a naming convention; modules are a contract. A modular monolith defines an explicit public interface per module and uses tooling such as Packwerk or Spring Modulith to fail the build when code bypasses it. Without enforcement, it is indeed just folders.

When do microservices still win?

When parts of your system have genuinely different scaling profiles, availability requirements, or team ownership at serious scale. A video transcoding pipeline and a settings page shouldn't share a deployment. DHH's "Majestic Monolith" essay puts it plainly: the pattern fits most teams, not all of them.

Your first five steps

1.       Map your business domains and sketch one module per domain, not per database table.

2.       Define each module's public interface and make everything else private by default.

3.       Add boundary enforcement to CI on day one, using Packwerk, Spring Modulith or architecture tests.

4.       Route cross-module communication through interfaces or in-process events, never shared tables.

5.       Revisit the map quarterly and only extract a module into a service when production evidence demands it.

If you're weighing this decision for your own product, BeyondPixl Studio runs architecture reviews that map your domains to enforceable module boundaries and give you a written extraction plan for the day you actually need one. Book a technical architecture review and get the seams right before they get expensive.

Ready to build something exceptional?

Let’s talk about your project.