What Is a Monolith, Really?

A monolith is a single deployable unit containing all the code for your application. The user authentication service, payment logic, notification system, and product catalog all live in the same process and are deployed together. A single git push ships the whole thing.

The term "monolith" has developed a negative connotation in the industry, but it is a valid, battle-tested architecture. Companies like Shopify, Stack Overflow, and Basecamp have run highly successful, large-scale products on well-structured monoliths for years. The monolith becomes a problem not because of its structure per se, but because of what happens when it grows without discipline: tight coupling, implicit dependencies, long build times, and the inability to scale specific bottlenecks independently.

What Are Microservices?

Microservices decompose an application into a collection of small, independently deployable services, each responsible for a single bounded context. The payment service, user service, notification service, and product catalog are all separate processes that communicate over a network (usually HTTP/REST or async message queues like Kafka or RabbitMQ).

The benefits are real: you can deploy the notification service twelve times a day without touching the payment service. You can scale the image processing service horizontally while keeping the rest of the system unchanged. Different teams can own different services with minimal coordination overhead. Different services can use different technology stacks where appropriate.

But these benefits come with significant operational overhead. You now have distributed tracing to set up, service discovery to manage, inter-service authentication to implement, and distributed transaction patterns (like sagas) to handle what used to be a simple database transaction.

The Real Cost of Microservices

The "microservices tax" is real and frequently underestimated:

  • Operational complexity — Each service needs its own CI/CD pipeline, health monitoring, logging, and deployment infrastructure.
  • Network failures — Function calls that used to be in-process and infallible are now network calls that can fail, timeout, or return partial results.
  • Distributed transactions — ACID transactions that span service boundaries require complex coordination patterns (2PC, saga pattern) that are hard to implement correctly.
  • Service discovery and load balancing — You need a service mesh or registry (Consul, Istio, Kubernetes native) to route requests between services.
  • Testing complexity — Integration testing across services requires contract testing (Pact) or maintaining full environment replicas.
  • Developer experience — New developers need to run 8 services locally to test a simple feature, instead of one process.

The Modular Monolith: The Best of Both Worlds

The false dichotomy between "a spaghetti monolith" and "fully decomposed microservices" ignores the most practical option for most teams: the modular monolith.

A modular monolith is a single deployable unit that enforces strict internal module boundaries. Modules (payment, auth, catalog) cannot access each other's internal implementation — they communicate only through defined interfaces. Database schemas are module-owned and not shared across modules. Dependencies are explicit and one-directional.

This gives you clean code organization, fast local development, simple deployment, and easy refactoring — while keeping the door open for extracting services later. Shopify's Modular Rails monolith and Basecamp's approach are real-world examples of this pattern at scale.

Decision Framework: When to Choose Each

✅ Choose a Monolith When

  • You are building v1 of a new product
  • Your team has fewer than 15 engineers
  • The domain is not fully understood yet
  • You do not have dedicated DevOps/SRE staff
  • Time to market is the priority
  • Your scaling challenges are uniform (not per-component)

✅ Consider Microservices When

  • Different components have vastly different scaling needs
  • You have multiple independent teams (2-pizza teams)
  • You need different tech stacks per component
  • You have a mature DevOps platform (K8s, observability)
  • Deployment coupling is genuinely slowing you down
  • Regulatory isolation requirements exist

How to Migrate a Monolith to Microservices (When You Are Ready)

The strangler fig pattern is the standard approach for safe monolith decomposition. Instead of a big-bang rewrite, you incrementally extract services from the monolith by routing specific endpoints to new services via an API gateway. The monolith continues to handle requests not yet migrated. Over time, the monolith "shrinks" as more functionality moves to dedicated services.

The key discipline: extract services along natural domain boundaries, not technical layers. Do not extract "the database layer" or "the API layer." Extract the notification system, the billing service, or the document processing pipeline — self-contained domains with clear inputs and outputs.

Also, decompose your data before (or simultaneously with) decomposing your code. Sharing a database between two "microservices" negates most of the independence benefits. Each service must own its data store, even if it is a separate schema in the same database server at first.

What We See in the Industry in 2025

The pendulum has swung. After years of "microservices for everything," the industry is more pragmatic. Amazon, one of the early microservices pioneers, published an internal analysis showing that a Prime Video feature migrated from microservices back to a monolith reduced infrastructure costs by 90%. DHH's "The Majestic Monolith" essay sparked a serious re-evaluation. Developers.io, Segment, and Uber have all published post-mortems on microservices complexity getting out of hand.

The modern consensus: start modular monolith, extract microservices only when you have a clear, demonstrated scaling or organizational reason to do so. The direction of migration is always easier than the reverse.

Conclusion

The microservices vs monolith decision is not about what is architecturally "better" in the abstract. It is about what is right for your team size, operational maturity, and the nature of your scaling constraints. For most teams and most products, a well-structured modular monolith is the superior starting point. Microservices are a powerful tool — but one that extracts a significant complexity tax that only pays off at the right scale and organizational maturity.

If you are facing this architectural decision for your product, Aidhunik can help you evaluate the trade-offs and design an architecture that matches your current needs while remaining migration-friendly.

Talk Architecture With Us
Back to all articles