Skip to main content

Microservices vs Modular Monoliths: Why You (Probably) Don't Need Kubernetes Yet

We all bought the microservices dream. Now we're dealing with the hangover. A pragmatic look at architecture for small teams.

Back to Blog
Published · 26 Jan 2026
9 min read Updated 20 Jun 2026 Jaanek Liiskmaa
/ Architecture DevOps
Microservices vs Modular Monoliths: Why You (Probably) Don't Need Kubernetes Yet

Five years ago, if you weren't splitting your application into a dozen microservices, you were treated like a dinosaur. The industry narrative was brutal: Monoliths were "legacy trash" to be escaped and Microservices were the promised land of infinite scalability.

We all bought the dream. We split our codebases, containerized everything and hired DevOps engineers.

Now it’s 2026 and the hangover has set in.

It’s not that microservices don’t work - they power Netflix and Uber for a reason. But for the rest of us? We learned the hard way that distributed systems turn logic problems into network problems. And network problems are the worst kind of problems.

The question today isn't "What is the most modern architecture?". It is: "What can my team actually maintain without burning out?".

The "Netflix Envy" Trap

Microservices solved a very specific problem for Silicon Valley giants: Organizational Scaling.

If you have 500 developers working on just the "Billing" module, you need hard boundaries so they don't break the code written by the 500 developers working on "Search". You accept the complexity of network calls because the alternative is developers stepping on each other's toes all day.

But if you have a team of five engineers? You don't have an organizational scaling problem. You have a focus problem.

When a small team adopts microservices too early, they stop building the product and start building infrastructure. Instead of shipping features, you spend your week debugging why Service A can't talk to Service B because of a slightly misconfigured generic type in a gRPC contract.

The Nightmare of Distributed State

Let's look at a real-world scar: E-commerce.

On a whiteboard, splitting "Orders", "Inventory" and "Payments" into separate services looks clean. Beautiful, even.

Then reality hits. A user buys a t-shirt.

  1. The Order Service creates the order.
  2. The Inventory Service reserves the item.
  3. The Payment Service... crashes. Or declines the card.

Now what? You have a "zombie" reservation. The stock is marked as sold, but no payment was captured.

You need to:

  • Send a compensating event to unreserve the stock
  • Handle the case where that event fails
  • Monitor for orphaned reservations
  • Debug across three different log streams

Monolith Scenario:

In a monolith, you wrap the order creation, inventory update, and payment call in a single database transaction.

Payment failed? The database automatically rolls back. The order never existed, the stock was never reserved. Done.

The Luxury of Consistent State

In a distributed system, this scenario is a headache. You successfully reserved the stock in Service B, but the payment failed in Service C. Now you have "zombie" inventory that no other customer can buy.

To fix this, you have to write a "compensating transaction" - an event that tells the Inventory Service to undo the reservation. But what if that message gets lost in the queue? Now your data is permanently inconsistent. You are debugging partial failures across three different log streams while support tickets pile up.

In a monolith, this problem becomes dramatically simpler and more predictable.

While the payment gateway is still an external dependency that can fail, your internal state remains under control. The Inventory and Order tables live in the same database. You can atomically reserve stock and create the pending order in a single ACID transaction.

  • Payment failed? Just catch the error and roll back the database transaction.
  • The order disappears, the stock is released instantly.

The Analytics Black Hole

Here is a problem architects rarely mention in the design phase: "The CEO wants a dashboard.".

In a Modular Monolith, if management asks for a report showing "Sales by Region vs. Inventory Levels", you write a SQL query. You join the Orders table with the Inventory table. It takes 15 minutes.

In a microservices architecture, you physically cannot join those tables. They live in different databases.

  • To build that simple dashboard, you now need a Data Lake.
  • You need ETL pipelines to sync data from Service A and Service B.
  • Now you have "Eventual Consistency" in your reports. The Sales team calls you asking why the dashboard says 100 items sold, but the inventory shows 105 items missing.

Microservices make individual writes scalable, but they make complex reads a nightmare.

The Invisible "Kubernetes Tax"

There is an economic cost that nobody puts in the pitch deck.

In a microservices architecture, every service is a "pet" that needs feeding. It needs a Dockerfile, a CI/CD pipeline, monitoring alerts and dependency updates.

This is usually where Kubernetes enters the chat. Strictly speaking, you don't need Kubernetes for microservices. But managing 15 different services, ports and configurations manually doesn’t scale. So you adopt an orchestrator. Now, instead of just maintaining your app, your team is maintaining a Control Plane, debugging Ingress controllers and managing Pod disruption budgets.

For a startup, this is a tax on your most limited resource: developer attention. If you have more YAML files than customers, you aren't an engineering team anymore. You are infrastructure shepherds.

The Case for the Modular Monolith

This isn't nostalgia. The return to Modular Monoliths is a rational response to complexity.

A Modular Monolith creates boundaries using code, not network cables. You still separate your "Billing" logic from your "Inventory" logic using folders, modules and strict interfaces (enforced by tools like ArchUnit). But you deploy them together.

Domain Discovery (The Learning Phase)

The most valuable thing a Modular Monolith buys you is cheap discovery. When you are building a new system, you rarely know exactly where the domain boundaries should be.

If you hard-code these boundaries into microservices early, refactoring them later requires cross-team coordination and data migrations. In a monolith, moving a concept from "Billing" to "Orders" is a simple code refactor. It allows you to draw your boundaries in pencil, not ink, until you are sure they are right.

The "Go to Definition" Superpower

Traceability matters. In a monolith, if you want to know how a function works, you Ctrl+Click (or Cmd+Click) in your IDE, and you are there. In a distributed system, Ctrl+Click takes you to an API client interface. To find the actual logic, you have to clone another repo.

Pick Your Poison: The Trade-Offs

Architecture is never about finding a perfect solution, it is about choosing your pain.

  • Microservices trade simplicity for isolation. You pay a high tax in operational complexity. In exchange, you get hard physical boundaries. One bad deploy in "Search" cannot crash the "Billing" server, because they run on different machines.
  • Modular Monoliths trade isolation for velocity. You gain the ability to move fast, test easily and report simply. But you pay with the Risk of Coupling.

If your team lacks the discipline to enforce boundaries in code, a modular monolith will eventually degrade into a "Big Ball of Mud" (Spaghetti code). However, experienced engineers know a secret: A Distributed Ball of Mud is much harder to clean up than a Monolithic one. You can’t fix bad code by adding network latency.

When Should You Actually Split?

So, is the monolith forever? No.

You should extract a microservice when-and only when-you have a specific problem that a monolith cannot solve. Usually, there are only three valid reasons:

  1. Divergent Scaling: One specific function (e.g., video processing) is consuming 90% of your CPU and starving the rest of the app. Move that and only that, to its own service.
  2. Security Isolation: You are handling credit card data and need to reduce the scope of your PCI compliance audit to a single, hardened box.
  3. Organizational Friction: It's not about a number, it's about signals. Are deployment queues blocking teams? Are merge conflicts becoming a daily ritual?

The reality is that most successful systems end up hybrid: a strong core monolith with a few carefully extracted services. This reassures pragmatic engineers that we aren't being dogmatic-we are just being smart about where we spend our complexity tokens.

If you don't have one of these problems, keep it simple.

Architecture is not about proving how smart you are. It’s about choosing the problems you want to live with. Personally? I’d rather debug code I can see than failures I can’t reproduce.

Frequently Asked Questions

What is a modular monolith?

A modular monolith is a single deployable application whose internal code is divided into strict domain modules with defined interfaces - but deployed as one unit, not as separate network services. It enforces boundaries through code conventions and tooling rather than through network calls and separate databases. You get the development simplicity of a monolith with much of the structural clarity of microservices.

When should you use microservices instead of a modular monolith?

There are three valid reasons: divergent scaling requirements (one function consumes 90% of CPU and needs to scale independently), security isolation (PCI compliance requires a hardened separate environment for card data), or genuine organisational friction (separate large teams are causing daily deployment conflicts). If none of these apply, a modular monolith delivers better velocity and easier debugging for the vast majority of teams.

What is the difference between microservices and a modular monolith?

Microservices separate concerns with network boundaries - each service has its own codebase, database, and deployment pipeline. A modular monolith separates concerns with code boundaries - modules have strict interfaces but share a database and deploy together. Microservices pay an operational tax in distributed tracing, network failures, and eventual consistency in exchange for physical isolation. Modular monoliths avoid that tax at the cost of requiring disciplined code boundaries.

What does "Kubernetes tax" mean?

The Kubernetes tax is the operational overhead of running a container orchestration platform that is disproportionate to a team’s actual scaling needs. Kubernetes solves real problems at hundreds of services. For teams with fewer than ten services, it typically means maintaining a Control Plane, debugging Ingress controllers, and managing Pod configurations - consuming developer time that would otherwise go toward the product.

When should you extract a service from a monolith?

Extract a service when you have a specific, demonstrated problem the monolith cannot solve - not because a service feels architecturally right. The clearest signal is a function that needs to scale or deploy on a fundamentally different schedule from the rest of the application. Premature extraction freezes your domain model at a point when you likely do not yet know where the real boundaries are.

Need help with your architecture?

We help engineering teams build reliable, scalable systems.

Let's Talk