Why I’d Choose a Modular Monolith Over Microservices

This Is an Opinion

This is not a tutorial — just a perspective shaped by building and operating production systems.

I’ve worked on systems that needed to process data reliably, handle failures gracefully, and keep things running without constant intervention. These systems weren’t trivial, but they also didn’t require massive scale or dozens of teams.

And in those situations, a modular monolith often turned out to be the better choice.


Microservices Aren’t Always the Answer

Microservices are powerful, but they come with real overhead:

  • More services to deploy and manage
  • More networking, coordination, and failure modes
  • More complexity in debugging and observability
  • Higher operational burden overall

For large organizations with many teams, this tradeoff can make sense.

For smaller teams or systems with predictable workloads, it often doesn’t.


What Is a Modular Monolith?

A modular monolith is a single deployable application with clear internal boundaries.

Instead of splitting everything into separate services, you organize your code into well-defined modules:

src/
├── services/
├── utils/
├── workers/
└── config/

Each module has a clear responsibility, but everything runs in one process (or a few processes), sharing the same database and runtime.

You still get separation of concerns — without the overhead of distributed systems.


The Simpler Approach

In many cases, a simpler stack can go a long way:

  • Database as coordination layer
    Use transactions and locking to manage work safely
  • Async I/O for concurrency
    Handle multiple tasks efficiently without complex worker systems
  • Basic process management
    Run a few well-defined services instead of orchestrating many
  • Minimal infrastructure
    Fewer moving parts → fewer things that can break

This approach reduces operational burden while still being reliable.


The Real Tradeoff

The key question isn’t “monolith vs microservices.”

It’s:

Do you actually need distributed complexity?

Microservices help when:

  • Different components need to scale independently
  • Multiple teams deploy independently
  • The system is very large or rapidly evolving

A modular monolith works well when:

  • The team is small to medium
  • The workload is predictable
  • Simplicity and reliability matter more than flexibility

Lessons Learned

A few principles that stood out to me:

  • Start simple — you can always split later
  • Design clear boundaries — even inside a monolith
  • Optimize for operability — not just architecture diagrams
  • Avoid premature complexity

A well-structured monolith is not a limitation — it’s often a strong foundation.


Conclusion

There’s a natural tendency in software engineering to reach for more complex architectures.

But complexity comes with a cost.

A modular monolith can be easier to build, easier to operate, and easier to reason about — especially for small teams and focused systems.

It may not look as impressive as a distributed architecture, but in many cases, it’s the more practical choice.

Leave a Reply

Your email address will not be published. Required fields are marked *