Community Article

Microservices vs Monolith: An Honest Comparison

Modular monolith is the right default for most teams. Microservices earn their cost only past a specific organizational scale, and the bar is higher than the literature suggests.

Microservices vs Monolith: An Honest Comparison

Modular monolith is the right default for most teams. Microservices earn their cost only past a specific organizational scale, and the bar is higher than the literature suggests.

microservices
monolith
system-design
trade-offs
backend
laylabauer

By @laylabauer

February 14, 2026

·

Updated May 18, 2026

558 views

17

Rate

My contrarian opinion, said out loud: most teams that adopted microservices in the last five years should not have. The split was premature, the operational overhead exceeded the team's capacity to absorb it, and the productivity loss showed up six months later as "why does it take three days to ship a one-line change?" The answer is: because the change touches three services, three deploy pipelines, three on-call rotations, three coupled API versions, and three test suites, and the team is small enough that one person owns all three.

I am writing this article because I have been on both sides. I have argued for microservices on a fast-growing team where they paid off, and I have argued against them on a small team where they did not. The honest comparison is not "microservices good" or "monolith good" but "the right answer depends on your organization, not your code". This article is the version of that comparison I would write if I were trying to talk a small team out of a premature split.

The case for microservices

Microservices are services that run in separate processes, communicate over the network, deploy independently, and own their data. The wins are real, in the right context:

  1. Independent deploys. Team A can deploy their service without coordinating with Team B. For organizations of fifty or more engineers, this is a real coordination saver.
  2. Independent scaling. The recommendations service is CPU-bound; the user service is memory-bound. Running them in the same monolith means scaling for the worst case of both. Splitting them lets each scale on its own dimension.
  3. Independent technology choices. The data team can use Python; the front-end-API team can use TypeScript; the search team can use Go. Each team picks what fits their problem.
  4. Failure isolation. A bug in the recommendations service does not crash the checkout service. In a monolith, an out-of-memory in any module brings down the whole process.
  5. Bounded ownership. Each service has a clear team. Code review, on-call, design discussions, all happen at service granularity. The team that owns the service is the team that gets paged when it breaks.

These are real wins. The list is not propaganda. The question is not whether microservices have benefits but whether your team's operational maturity and organizational scale justify the cost.

The case for the monolith (or the modular monolith)

A monolith is a single deployable unit, usually one process, usually one database. The wins:

  1. One deploy, one rollback. The pipeline is simple. A bad deploy is reverted by deploying the previous version. No coordination needed.
  2. In-process function calls instead of network calls. No serialization, no network latency, no flaky integration tests, no service-to-service auth.
  3. Cross-cutting refactors are easy. Changing a function signature ripples through the codebase via the compiler. In a microservices world, the same change requires a coordinated release across services and a versioned API.
  4. One database transaction. Operations that span multiple modules can be atomic without sagas, without two-phase commit, without compensations.
  5. One on-call rotation. Smaller teams can stay sane.

The modern version of the monolith argument is the modular monolith: one deployable unit, but with strict module boundaries, each module owning its own tables, communicating through well-defined interfaces. The internal structure looks like microservices; the deployment looks like a monolith. This is the architecture I would default to today for any team smaller than thirty or forty engineers.

The hidden costs of microservices

The advertised wins of microservices come with costs that are easy to underweight:

Microservices operational costs
  - service discovery (consul, k8s services, custom registry)
  - distributed tracing (jaeger, zipkin, datadog APM)
  - centralized logging (ELK, loki, splunk)
  - service-to-service auth (mTLS, JWT, service mesh)
  - schema management for cross-service contracts
  - distributed transaction patterns (sagas, outbox)
  - per-service deploy pipeline, on-call rotation, monitoring dashboard
  - cross-team coordination on breaking API changes

Each item on that list is real engineering effort. Building the platform layer that makes microservices viable can take a dedicated team of two to four engineers for six to twelve months, full-time. That team's salary is a cost that the productivity gains of microservices need to overcome.

In an organization of two hundred engineers, the platform-team cost is amortized over the productivity gains of forty teams shipping independently. The math works. In an organization of ten engineers, you do not have a platform team; you have ten engineers half-doing platform work alongside their feature work, and the productivity math works against you.

The numbers I would want before splitting

When I am asked "should we split this monolith?", three numbers I want to see first:

  1. Engineer count. Below twenty, I default to monolith. Twenty to forty, I default to modular monolith. Forty to a hundred, I evaluate splitting one or two clear domains. Above a hundred, microservices are usually right somewhere.
  2. Independent deploy frequency. If the monolith deploys once a day with no team coordination, splitting will not improve velocity much. If it deploys once a week because every change needs sign-off from three teams, splitting might help. The bottleneck is organizational coordination, not technology.
  3. Scaling pain. If one module is genuinely the bottleneck and other modules are not, splitting it lets you scale that one module independently. If everything scales the same way, splitting saves nothing.

If all three numbers point toward splitting, microservices may be worth the investment. If two or fewer do, I would bet on the modular monolith.

A real conversation with an eight-engineer startup CTO

A real conversation I had with a startup CTO. They had eight engineers and a monolith with four modules: users, payments, recommendations, and reporting. They wanted to split into four services. My questions:

  • How often does the monolith deploy? Three times a week.
  • Is deploy frequency limited by team coordination or test runtime? Test runtime, mostly.
  • Is any module a scaling bottleneck? No.
  • Is any module owned by a separate team? No, every engineer touches every module.
  • What productivity bottleneck would splitting solve? Unclear.

That last answer is the tell. They wanted microservices because the literature said modern systems use microservices, not because they had identified a bottleneck microservices would solve. The right move was to fix their test runtime (parallelize, skip slow integration tests in pull requests, run them nightly) and stay on the monolith. They did, and shipped 50% more features the following quarter.

The lesson: the technology you choose should be the technology that solves your actual problem. "We want to be modern" is not an actual problem.

The case I will defend for splitting

I do split monoliths sometimes. The conditions:

  1. Two or more teams are blocked on each other's deploys. When team A's release is blocked because team B's tests are flaky in the shared pipeline, splitting B's module into its own service unblocks A. This is the most common legitimate reason.
  2. One module has fundamentally different scaling characteristics. The transcoding service is CPU-bound and you want to scale it on a hundred GPU nodes; everything else runs on three CPU nodes. Splitting the transcoder lets each scale appropriately.
  3. One module has fundamentally different reliability requirements. The login flow needs 99.99% uptime; the analytics ingestion can tolerate hours of downtime. Splitting them lets each have its own deploy cadence and its own SLO.
  4. Acquired a system written in a different stack. A Python ML team and a Go infrastructure team cannot be in the same monolith without one of them switching languages. Microservices are the natural answer to language heterogeneity.

If your reason is on this list, the split is probably justified. If not, I would push back hard.

The modular monolith pattern I steal from teams that do this well

Three rules that make a monolith feel like microservices without the operational cost:

  1. Each module owns its own database tables. Cross-module reads go through the module's public interface, not by joining tables directly. This is enforced by code review, not by the database, but it is enforceable.
  2. Modules communicate through interfaces, not by reaching into each other. The user module exposes getUserById, updateUserEmail, and similar; other modules call those functions. If a function does not exist for the cross-module use case, you add it (and discuss whether it should). This is the same discipline you would have for a service-to-service call but cheaper to enforce.
  3. No shared mutable state outside of databases. No global singletons that two modules write to. No in-memory caches that cross module boundaries. Everything that crosses a boundary goes through an explicit interface.

A modular monolith with these rules is structurally similar to microservices but pays none of the network, deploy, or coordination costs. When the time comes to actually split (and it might), the split is a refactor, not a rewrite, because the boundaries are already in place.

Hybrid is the actual common architecture

Most production systems I have seen are not pure monolith or pure microservices. They are hybrid: one or two big monoliths plus a handful of focused services for specific reasons. The auth service is split because it has different security requirements; the search service is split because it scales differently; the rest is in a monolith.

This is fine. It is, in fact, what mature systems look like. The dichotomy in the literature is rhetorical; the practical answer is "split when there is a reason; do not split when there is not". Drawing a line at "100% microservices" or "100% monolith" does not match reality.

What I would tell a team adopting either

For a team considering microservices: name the productivity bottleneck or the scaling bottleneck microservices will solve. If you cannot name it, do not split. If you can, scope the split to the single module that has the bottleneck; do not split the whole monolith at once.

For a team committed to a monolith: commit to the modular monolith discipline. Module boundaries enforced in code review, separate database tables per module, explicit interfaces between modules. The discipline is the difference between a maintainable monolith and a ball of mud, and the discipline is much cheaper than microservices.

For both: invest in the things that microservices and monoliths both need. Logging, monitoring, alerting, deploy pipelines, on-call rotations, runbooks. Those are not architecture choices; they are the foundation any architecture sits on.

Default to the modular monolith. Earn the split.

The microservices movement was a reaction to the ball-of-mud monoliths of the early 2010s, and the reaction was correct: those monoliths were unmaintainable and splitting them was the way out. The mistake the industry made afterward was generalizing the lesson: "microservices fixed our ball of mud, so all teams should use microservices." That is not the lesson. The lesson is: "unmaintainable architecture is bad, and there are multiple ways to be maintainable." The modular monolith is one way. Microservices are another. Pure unstructured monoliths are not maintainable; neither are pure microservices without a platform team. Pick the one that matches the size and operational maturity of your team, not the one that matches the architecture diagrams in your favorite tech blog.

Back to Articles