← Back to list

Microservices vs. Monolith: The Tradeoffs No One Warned You About

Every architecture decision is a bet on the future. Most architects lose that bet — not because they chose wrong, but because they chose…

Gaurav Ranjan · 2026-06-09 12:33 · 0 claps · 10.3 min read
#system-design-concepts #microservices #software-architecture #azure #software-engineering
Open on Medium ↗
Wiki topics: ☁️ · DevOps & Cloud 🏛️ · Architecture

Microservices vs. Monolith: The Tradeoffs No One Warned You About

Every architecture decision is a bet on the future. Most architects lose that bet — not because they chose wrong, but because they chose without understanding what they were trading away.

I have been in meetings where a team of 6–8 engineers decided to build microservices on day one, convinced they were being “enterprise-grade.” And I have also seen the opposite, a big org with 200+ people struggle under a monolith so tangled that deploying a single feature took three weeks of coordination.

Both situations were painful. and honestly both could’ve been avoided. And both happened because the team chose an architecture pattern without understanding what they were trading away.

What makes things more interesting today is that we now have real-world lessons from companies that actually got this wrong and then fixed it. Think about names like Amazon, Segment, Istio, InVision, Shopify. Their real-world experiences teach way more than any textbook ever could.

A monolith ships as one unit; microservices ship independently. That single difference cascades into everything else — and the tradeoffs are rarely what you expect.

⏱ The 60-Second Baseline

Before we go further, a quick level-set.

Monolith: A single deployable unit. All modules — auth, orders, payments, catalogue — live in one codebase and ship together. Communication is in-process (function calls, not HTTP). One database, one tech stack, one deployment pipeline. Can still be well-structured internally — that’s a modular monolith, and it’s very different from a big ball of mud.

Microservices: Multiple independently deployable services, each owning a single bounded context. Communication is over the network (REST, gRPC, or events). Each service owns its own data store. Polyglot by nature — different services can use different languages and databases. Requires explicit API contracts between services.

Modular monolith: One deployable unit, but with strict internal module boundaries enforced by the build system or architecture rules. The best of both worlds when done right — Shopify’s entire platform runs this way.

One word of precision: “micro” in microservices refers to scope of responsibility, not lines of code. A service owning all of Order Management might have 50,000 lines of code and still be a valid microservice.

⚖️️ The Real Tradeoffs

Deployment independence costs more than you think

The headline promise of microservices is that teams deploy independently. In theory, a superpower. In practice, you need containerisation, orchestration (AKS), separate CI/CD pipelines per service, contract testing, a service mesh, distributed tracing, centralised logging, and an API gateway — eight significant engineering capabilities before you ship a single business feature.

⚠️ Organisations adopting microservices prematurely spend 35–50% of engineering time on infrastructure in the first 18 months, versus under 10% on a well-structured monolith.

Scalability comes with a new class of failures

Microservices let you scale hot components independently - scale your Catalogue to 50 pods during a flash sale while Payments stays at 4. Monoliths force you to scale everything, even the idle parts.

But distributed systems introduce failures that simply don’t exist in monoliths: network partitions, cascading timeouts, message ordering issues, the dual-write problem. Your engineers now need to understand eventual consistency, idempotency, and saga patterns for what used to be simple CRUD operations.

The Fallacies of Distributed Computing are not theoretical issues. They are the bugs that may wake you up at 3 AM.

Team autonomy sounds great, but it comes with a hidden cost

Conway’s Law is very real - your system design ends up looking like how your teams are organized. But the moment one service needs to talk to another (like Checkout calling Inventory), things get complicated. Now you need: API contracts, versioning rules, deprecation plans, contract tests, cross-team SLAs. And that’s just for one interaction.

Do this across 50 services, and suddenly you’ve built a whole layer of coordination and process overhead. In many cases, this slows you down more than a simple monolith ever did.

I’m not trying to discourage using microservices - the goal here is just to highlight the trade-offs so you can make a more informed decision.

🏢 What Real Companies Learned the Hard Way

The Reversal: Microservices → Monolith

Amazon Prime Video (2023) | 90% cost reduction

In March 2023, Prime Video senior engineer Marcin Kolny published a case study that shook the industry. Their video quality analysis tool - which monitored every user stream for defects across thousands of concurrent streams - had been built as a distributed microservices system orchestrated by AWS Step Functions.

The problem: Step Functions was a scaling bottleneck. The system performed multiple state transitions per second of stream. Every state transition had a cost. The architecture was both expensive and hitting hard scale limits.

“We realized that the distributed approach wasn’t bringing a lot of benefits in our specific use case, so we packed all the components into a single process.” - Marcin Kolny, Amazon Prime Video

The result: infrastructure costs dropped by over 90%, and scaling capabilities actually improved. This was Amazon — the company that practically invented the modern microservices pattern — publicly saying their distributed system was over-engineered for this specific workload.

The nuance most people miss: This wasn’t Amazon abandoning microservices. It was one team fixing one over-granularised tool. The lesson is about right-sizing, not wholesale reversal.

Segment | 140+ services consolidated

Segment was running 140+ microservices. Teams spent more time managing Kubernetes configurations than building features. The operational overhead had become the main work rather building the product. They consolidated related services into domain-based modules — not a pure monolith, but dramatically reduced service count — and reported a 50% reduction in operational complexity alongside measurably faster feature development and easier onboarding for new engineers.

InVision | The people and dollars problem

InVision had a legacy team with fewer people but more repositories, databases, and programming languages than was sustainable. Conway’s Law cut both ways: the architecture that made sense when the team was large became a burden as the team shrank. They explicitly called out that “microservices also have a dollars-and-cents cost” — each service runs servers, talks to databases, reports metrics, generates logs, and requires redundancy for high availability. All of that costs money, continuously. They merged microservices back into a monolith to match their actual team size and economics.

The Forward: Monolith → Microservices (Still the Right Call in Context)

LinkedIn, Uber, Spotify | The canonical cases

These companies started as monoliths and migrated to microservices as they hit genuine, measurable limits — not because it was fashionable. LinkedIn decomposed into services like User Registration, User Profile, and Post Service, each with its own database and communicating via APIs and Kafka. Uber started as a single-city San Francisco service, and the original monolith simply couldn’t support global multi-product scale. Spotify used microservices to let squads own their domains end-to-end.

The critical context: these companies had thousands of engineers, mature DevOps platforms, and years of investment in observability before their microservices architectures stabilised. That context gets dropped when others blindly copy the pattern.

Shopify | The modular monolith middle path

Shopify, with its massive Ruby on Rails codebase built over a decade by thousands of developers, considered microservices as it scaled but also recognised their challenges. Instead, Shopify evolved into a modular monolith — keeping the codebase unified but enforcing strict boundaries between components like Orders, Shipping, and Billing. They retained the simplicity of a monolith while introducing the modularity needed for scale. Today Shopify powers millions of merchants on this architecture. It is one of the cleanest examples of the hybrid approach done right.

⚠️ Mistakes Architects Actually Make

Mistake #1: Premature decomposition

A fintech startup decided on day one they needed 22 microservices because their target market was “enterprise scale.” Six engineers. No product-market fit. Six-month runway, and within 90 days they’d burned most of it on infrastructure, shipped zero customer-facing features, and were still debating how UserProfile should talk to KYC.

They were doing what Segment later had to undo — creating distributed complexity before they had a product that warranted it.

The fix: Don’t jump straight to microservices. Start with a modular monolith. Keep things cleanly separated- like Orders, Payments, Catalogue - with clear interfaces. Only break something out when it actually needs to scale or run independently. That’s basically the Strangler Fig approach where you replace it piece by piece while it’s still running. Shopify does this.

Mistake #2: The distributed monolith

This is a sneaky but common mistake. Teams break a system into services but keeps them tightly chained together: Order calls Inventory, which calls Pricing, which calls Catalogue. You need all four available for any user action to succeed. A schema change in Catalogue still requires coordinated deployment across all four.

So, in reality, nothing is independent. You’ve just recreated a monolith but now with the added headache of managing multiple services. This setup is actually worse than both a proper monolith and proper microservices. And this is exactly the kind of issue Prime Video ran into before they simplified things.

The fix: Start with good domain boundaries. Then use event-driven communication for cross-domain operations — Order publishes OrderPlaced to Azure Service Bus; Inventory and Payments subscribe independently. Use direct/ synchronous calls only for queries within the same bounded context.

Mistake #3: Shared database across services

The shortcut that kills you slowly. Services are separated; the database is not. Every schema change becomes a coordination exercise. Services start reading each other’s tables, so they’re secretly coupled again. And if one service runs a bad query, it slows things down for everyone.

I’ve seen this in 3 out of every 5 microservices migrations I’ve reviewed. Multiple databases with unclear ownership — as result the microservices become unmanageable.

The fix: Give each service its own database, no sharing. Cross-service data goes through the API. Yes, you lose cross-service JOINs but that’s the point. Build the views you need using CQRS-style read models. Let services own their data and expose it properly.

Mistake #4: Ignoring operational readiness

One enterprise retail team spent 18 months moving to microservices. The code looked great. The architecture diagrams looked even better. When they went live, the on-call team couldn’t debug a failed order. The request had gone through 9 different services. Logs were scattered across 4 different systems. There was no clear map showing how things were connected. What used to take 15 minutes to debug in the monolith now took 2+ hours in the “modern” system.

So yeah, technically modern, but operationally painful.

The fix: Observability is a day-0 concern, not something to look at later. Wire up distributed tracing with Azure Monitor and Application Insights before you write service code. Structured logging with correlation IDs so you could trace a request end-to-end. Define SLOs per service and put them on a dashboard the on-call team sees first.

🔀 The Hybrid: When the Answer is Both

The best systems borrow deliberately from both patterns — applying each where it offers genuine advantage.

A better approach is not “all microservices” or “all monolith” but it’s a mix. In a financial services platform I know of, they kept the core ledger and transaction processing as a monolith. Why? Because consistency there is critical — you can’t afford things going out of sync. But everything around it was broken out into services — Reporting, Fraud Detection, and Notification Delivery. These ran as separate microservices and just listened to events coming from the core system. So, the core stayed simple, stable, and consistent. The surrounding parts got the flexibility to scale and evolve independently. Amazon’s broader platform works this way only — Prime Video consolidating one tool didn’t change the fact that their recommendation engine, payments, and streaming infrastructure remain distributed.

Modular monolith as the durable middle ground. Look at companies like Shopify (huge scale, millions of merchants), Stack Overflow (billions of page views), or Basecamp. These teams didn’t “forget” to move to microservices. They chose not to. Instead, they built a single system, but kept it cleanly broken into modules with strict boundaries. Each part is isolated inside the codebase, even though it runs as one app. And the result? They often ship faster and deal with way less complexity than a lot of microservices setups I’ve seen.

🗺️ Suggested Decision Framework

To make this easier to apply, here is a simple way to think about the architecture choice. There is no single “right” answer for every team. The better choice depends on team size, maturity, domain boundaries, scaling needs, and operational capability.

  1. Small team, early-stage product Start with a Monolith

Move fast first. A monolith is easier to build, test, deploy, and change when the team is small.

2. Multiple teams working on clearly different domains → Use Microservices

When each team owns a separate business area, microservices help teams work independently with fewer dependencies.

3. Some modules need very different scaling → Use Microservices for those modules

If one part of the system needs heavy scaling and others do not, separate that part instead of scaling the whole application.

4. Existing monolith has a few painful areas → Hybrid / Extract gradually

Do not rebuild everything. Pick the problem areas and slowly move them out using the Strangler Fig approach.

5. Stable core with frequently changing outer features → Hybrid Architecture

Keep the core simple as a monolith, and move flexible or event-driven parts outside as separate services.

6. No CI/CD, containers, monitoring, or DevOps maturity → Monolith first

Microservices without good operations support can create more problems than they solve.

7. Team is getting smaller but service count is high → Consolidate services

Too many services for a small team increases maintenance load. Service count should match team capacity.

Simple Rule of Thumb

  • Start simple. Split only when there is a strong reason.
  • Use monolith when speed, simplicity, and low operational effort matter most.
  • Use microservices when team ownership, independent scaling, and independent releases clearly justify the added complexity.
  • Use hybrid when only some parts of the system need separation.

✅ Before You Decide — A Practical Checklist

Before choosing microservices, ask a few simple questions:

🔲 Do we clearly know which non-functional problem microservices will solve right now?

🔲 Have we properly identified the business domains and service boundaries?

🔲 Do we already have CI/CD, monitoring, logging, and distributed tracing planned?

🔲 Have we calculated the extra operational cost of running many services on Azure?

🔲 Have we considered a modular monolith before jumping to microservices?

🔲 Does the team understand patterns like Saga, CQRS, and Outbox?

🔲 Does our current team size really support the number of services we want to run?

If most answers are no, then a monolith or modular monolith is probably the better choice for now.

You can always revisit the decision after 6 months. Architecture decisions are not permanent. But operational problems can stay for a long time if the foundation is weak.

The goal is not to proudly say, “we use microservices.” The goal is to deliver business value reliably; at the speed your team can actually support. Architecture is only a means to that goal, not the goal itself.

Companies like Netflix, Amazon, and Uber did not start with the kind of architecture we admire today. They started simpler. Their architectures evolved over years, based on real pain, real scale, and real measurement.

That is an important lesson.

Microservices are not bad. Monoliths are not outdated by default. The real question is: what is right-sized for your team, product, and business stage?

The smarter approach is to keep strong boundaries, split only where it makes sense, and accept distributed systems only when the benefits clearly justify the complexity.

That is where modern architecture is heading — not blindly back to monoliths, and not blindly toward microservices, but toward practical, right-sized architecture.


메타데이터
post_id
c30d31c4cd70
slug
microservices-vs-monolith-the-tradeoffs-no-one-warned-you-about-c30d31c4cd70
url
https://medium.com/@granjan.officialid/microservices-vs-monolith-the-tradeoffs-no-one-warned-you-about-c30d31c4cd70
canonical_url
https://medium.com/@granjan.officialid/microservices-vs-monolith-the-tradeoffs-no-one-warned-you-about-c30d31c4cd70
author_url
https://medium.com/@granjan.officialid
status
ok
fetched_at
2026-06-10 08:17:25