‹ Back to Blog Engineering

Microservices vs Monoliths: Making the Right Choice

April 5, 2026 · 9 min read
Cloud architecture

For the better part of a decade, the software industry treated microservices as the default architecture for any serious application. Monoliths were considered legacy, a sign that your team had not kept up with modern practices. In 2026, the pendulum has swung back, and the industry consensus is more nuanced. Both architectures have their place, and the choice between them should be driven by your specific constraints, not by fashion.

At Pepla, we have built and maintained systems on both ends of the spectrum. We have decomposed monoliths into microservices for clients who genuinely needed it, and we have consolidated microservice sprawl back into modular monoliths for clients who did not. This article shares the framework we use to make that decision.

The Monolith-First Approach

Martin Fowler's "MonolithFirst" advice from 2015 has aged remarkably well. The argument is simple: start with a monolith, establish clear module boundaries within it, and extract services only when you have a proven need. The reasoning is sound:

A modular monolith preserves the option to decompose later -- with the benefit of hindsight about real boundaries.

System architecture
A modular monolith with clear boundaries is not technical debt. It is a pragmatic architectural choice that preserves the option to decompose later, with the benefit of hindsight about where the real boundaries are.

What a Good Monolith Looks Like

A well-structured monolith is not a big ball of mud. It has clear internal module boundaries, enforced through the type system, package structure, or architectural fitness functions. Each module owns its data, exposes a defined interface, and does not reach into another module's internals. The fact that these modules happen to run in the same process does not mean they are coupled.

Languages and frameworks have caught up with this approach. .NET's modular monolith patterns, Java's module system (JPMS), and Go's internal package convention all provide mechanisms for enforcing boundaries within a single deployment unit.

When Microservices Add Genuine Value

Microservices solve specific problems. If you do not have these problems, they add complexity without benefit:

Notice that none of these drivers are about code quality, testability, or "modern architecture." A monolith can be perfectly testable and well-architected. If you are reaching for microservices to solve code quality problems, you are solving the wrong problem with the wrong tool.

Start monolith-first and extract services only when the complexity tax is clearly justified.

The Complexity Tax of Distributed Systems

Every network call introduces failure modes that do not exist in a function call. This is not theoretical; it is the daily reality of operating microservices.

Server cluster

The Eight Fallacies of Distributed Computing

Peter Deutsch's fallacies, written in 1994, remain brutally relevant:

Each of these fallacies translates to concrete engineering work: retry logic, circuit breakers, timeout configuration, distributed tracing, service mesh management, and more. This work does not ship features. It is the tax you pay for distribution.

Communication Patterns: Synchronous vs Asynchronous

How services communicate is as important as how they are decomposed.

Synchronous Communication (REST, gRPC)

Synchronous calls are simple and intuitive. Service A calls Service B and waits for a response. The problems emerge under load and failure:

Asynchronous Communication (Events, Message Queues)

Asynchronous patterns decouple services in time. Service A publishes an event and moves on. Service B processes it when ready. This eliminates cascading failures and temporal coupling, but introduces its own challenges:

Our recommendation at Pepla: use synchronous communication for queries (reads) and asynchronous communication for commands (writes). This pattern, sometimes called CQRS-lite, gives you the simplicity of synchronous reads with the resilience of asynchronous writes.

Architecture follows organisation -- align service boundaries with team boundaries.

Data Ownership and the Database Question

In a monolith, all modules share a database. In microservices, each service owns its data and exposes it only through its API. This is perhaps the most impactful difference between the two architectures.

Shared databases are simple but create tight coupling. A schema change in one module can break another. Data integrity is maintained through database transactions, which is straightforward. Reporting and analytics can query across all data directly.

Service-owned databases provide autonomy but create complexity. Cross-service queries require API calls or data replication. Distributed transactions across services are notoriously difficult and best avoided. The saga pattern provides eventual consistency for multi-service operations, but it is significantly more complex than a database transaction.

For many projects, a middle ground works well: a shared database with schema-level isolation. Each module owns its schema and accesses other modules' data only through defined views or APIs. This provides most of the autonomy benefits without the operational complexity of multiple database instances.

Conway's Law: Architecture Follows Organisation

Melvin Conway observed in 1967 that organisations produce system designs that mirror their communication structures. This is not just an observation; it is a force. If you have a single team, a monolith is the natural architecture. If you have multiple autonomous teams, microservices align with their organisational boundaries.

The inverse Conway manoeuvre, deliberately structuring teams to produce a desired architecture, is a powerful technique. If you want microservices, organise teams around service boundaries. If you want a modular monolith, organise teams around feature areas with shared codebase ownership.

Trying to run microservices with a monolithic team structure (one team responsible for all services) gives you the worst of both worlds: distributed complexity with centralised bottlenecks.

Architecture should follow team structure -- microservices with a monolithic team gives you the worst of both worlds.

A Decision Framework

Based on our experience delivering both architectures, here is when we recommend each:

Start with a modular monolith when:

Consider microservices when:

The right architecture is the one that lets your team ship reliable software efficiently. Not the one that looks best on a conference slide.

Need help with this?

Pepla helps teams choose and implement the right architecture for their scale. Let us assess your needs.

Get in Touch

Contact Us

Schedule a Meeting

Book a free consultation to discuss your project requirements.

Book a Meeting ›

Let's Connect