Back to blog
System Designintermediate

Microservices β€” What They Are and What They Actually Cost

Understand what microservices are, why companies move to them, and what they actually cost in operational complexity, distributed transactions, testing overhead, and team effort. Learn when NOT to use them.

LearnixoApril 15, 20268 min read
MicroservicesSystem DesignArchitectureDistributed SystemsMonolithModular Monolith
Share:𝕏

Microservices are simultaneously one of the most powerful architectural patterns and one of the most over-applied. Teams adopt them to solve scalability problems, then find themselves fighting distributed systems complexity instead.

This article gives you a realistic picture of what microservices are, what they cost, and when to actually use them.


What Microservices Actually Are

A microservice is an independently deployable service that owns a single business capability and its data.

The key words are:

  • Independently deployable β€” you can deploy the Order service without touching the User service
  • Single business capability β€” not "the notification code" or "all database operations", but "manage customer orders"
  • Owns its data β€” the Order service has its own database that no other service touches directly
Monolith:
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    E-commerce App                    β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚  Orders  β”‚ β”‚ Products β”‚ β”‚  Users   β”‚ β”‚Paymentsβ”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β”‚
β”‚       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β”‚
β”‚                   Shared Database                    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Microservices:
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Orders  β”‚  β”‚ Products β”‚  β”‚  Users   β”‚  β”‚Payments β”‚
β”‚  Service β”‚  β”‚  Service β”‚  β”‚  Service β”‚  β”‚ Service β”‚
β”‚   [DB]   β”‚  β”‚   [DB]   β”‚  β”‚   [DB]   β”‚  β”‚  [DB]   β”‚
β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜
     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                   Message Bus / API calls

The Two-Pizza Rule

Jeff Bezos's rule of thumb: a microservice team should be small enough to be fed by two pizzas (6-8 people).

This isn't really about pizza β€” it's about cognitive load and communication overhead. A small, autonomous team can:

  • Own their service end-to-end
  • Deploy independently without coordinating with 10 other teams
  • Make technology decisions appropriate for their service

If your microservice requires a 20-person team to operate, it's probably not a microservice β€” it's a mini-monolith.


Why Companies Move to Microservices

1. Team Autonomy and Parallel Deployment

In a monolith, all teams share one deployment pipeline. The team that deploys a bug fixes blocks everyone's release.

Monolith release train:
  Orders team ready βœ“
  Products team ready βœ“
  Users team ready β€” BUG FOUND βœ—
  β†’ Everyone waits for Users team to fix it

Microservices:
  Orders service deploys independently β€” done
  Products service deploys independently β€” done
  Users service has a bug β€” only Users is blocked, others unaffected

2. Independent Scaling

Different services have different load profiles. Microservices let you scale what needs scaling.

E-commerce peak traffic:
  Product Search: 50,000 QPS β€” needs 20 instances
  Order Checkout: 500 QPS β€” needs 2 instances
  Admin Panel: 10 QPS β€” needs 1 instance

With microservices: scale Product Search to 20 instances
With monolith: must scale the entire app to handle Product Search load
              (20 instances of admin panel code nobody uses)

3. Technology Flexibility

Different services can use different languages and databases.

  • ML inference service in Python (sklearn/PyTorch)
  • Core API in .NET/C# (team expertise)
  • Real-time service in Go (performance-critical)
  • Each using the database best suited to its access patterns

4. Fault Isolation

A crash in the Recommendations service doesn't take down the Order service.

Monolith: memory leak in Recommendations β†’ OOM β†’ entire app dies
Microservices: Recommendations crashes β†’ Order/Checkout still serve traffic

What Microservices ACTUALLY Cost

This is where most tutorials stop telling the truth. Microservices are not free.

1. Network Latency

Every service call that was a function call is now a network hop.

Monolith:
  orderService.getUser(userId)  β†’ in-memory call β†’ 0.001ms

Microservices:
  GET http://user-service/users/123 β†’ HTTP round trip β†’ 0.5–5ms
  (in same datacenter, under normal load)

An operation that chains 5 services may add 5–25ms of network latency. At 99th percentile (with retries, timeouts), this compounds significantly.

2. Distributed Transactions

In a monolith, wrapping a multi-step operation in a database transaction is trivial.

C#
// Monolith β€” dead simple
using var tx = db.BeginTransaction();
inventory.Decrement(productId, quantity);
order.Create(userId, items);
payment.Charge(userId, total);
tx.Commit();  // all or nothing

In microservices, you don't have a shared database or shared transaction:

Create Order involves:
  1. Order Service: create order record
  2. Inventory Service: decrement stock
  3. Payment Service: charge card
  4. Notification Service: send confirmation email

What happens if Payment fails after Inventory decremented?
What if Notification fails after Order was created?
β†’ You need Saga pattern (compensating transactions)
β†’ This is exponentially more complex

3. Operational Complexity

Monolith:
  Deploy: 1 thing
  Monitor: 1 thing
  Debug: 1 log stream

50 microservices:
  Deploy: 50 pipelines, dependency ordering, versioning
  Monitor: distributed tracing, aggregated logs, service mesh metrics
  Debug: a request touching 8 services across 50 log streams
         (you need correlation IDs, distributed tracing - OpenTelemetry)

You will need:

  • Container orchestration (Kubernetes)
  • Service discovery
  • Distributed tracing (Jaeger, Zipkin, Azure Monitor)
  • Centralized logging (ELK, Loki)
  • Service mesh (Istio) or at minimum consistent retry/circuit breaker libraries

4. Testing Complexity

Unit testing individual services is easy. But integration testing across services requires:

  • Each service running locally or mocked
  • Consumer-driven contract tests (Pact)
  • End-to-end test environments that mirror production
  • Test data management across multiple databases
Test "create order" in microservices:
  - Need User Service running
  - Need Inventory Service running
  - Need Payment Service (mocked or running)
  - Need Notification Service (mocked)
  - Need a message broker running (Kafka/RabbitMQ)
  - Need all their databases seeded
  
vs. monolith: spin up one process and one database

5. The Distributed Systems Fallacies

Peter Deutsch's eight fallacies that developers new to distributed systems always hit:

1. The network is reliable
2. Latency is zero
3. Bandwidth is infinite
4. The network is secure
5. Topology doesn't change
6. There is one administrator
7. Transport cost is zero
8. The network is homogeneous

With a monolith, these fallacies don't bite you β€” everything is in-process. With microservices, every one of them becomes a real engineering problem.


The Modular Monolith β€” The Often-Better Alternative

A modular monolith is a single deployable unit organized into strict modules with enforced boundaries.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    E-commerce App                    β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚  Orders  β”‚ β”‚ Products β”‚ β”‚  Users   β”‚ β”‚Paymentsβ”‚ β”‚
β”‚  β”‚  Module  β”‚ β”‚  Module  β”‚ β”‚  Module  β”‚ β”‚ Module β”‚ β”‚
β”‚  β”‚          β”‚ β”‚          β”‚ β”‚          β”‚ β”‚        β”‚ β”‚
β”‚  β”‚  Public  β”‚ β”‚  Public  β”‚ β”‚  Public  β”‚ β”‚ Public β”‚ β”‚
β”‚  β”‚  API     β”‚ β”‚  API     β”‚ β”‚  API     β”‚ β”‚ API    β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚         Modules communicate through interfaces only  β”‚
β”‚                (no internal class access)            β”‚
β”‚                   Shared Database                    β”‚
β”‚         (separate schemas per module)                β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Benefits you get:

  • Team autonomy (each team owns their module)
  • Clear boundaries (can extract to microservice later)
  • Simpler deployments (one thing to deploy)
  • Simpler transactions (shared DB)
  • Simpler testing

What you sacrifice:

  • Independent scaling per module
  • Technology freedom
  • Fault isolation (one crash still takes down everything)

The modular monolith is often the right choice for teams under ~30 engineers, or when the business domain isn't fully understood yet (premature service splitting is painful).


The Strangler Fig Pattern β€” Migrating to Microservices

The strangler fig tree grows around another tree, slowly replacing it. The pattern mirrors this.

Phase 1: Monolith handles all traffic
  Client β†’ API Gateway β†’ Monolith β†’ DB

Phase 2: New service handles one capability
  Client β†’ API Gateway β†’ /users/* β†’ User Service (new)
                      β†’ /*       β†’ Monolith (legacy)

Phase 3: More capabilities migrated
  Client β†’ API Gateway β†’ /users/*    β†’ User Service
                      β†’ /products/* β†’ Product Service
                      β†’ /orders/*   β†’ Monolith (shrinking)

Phase 4: Monolith deprecated
  Client β†’ API Gateway β†’ routes all to microservices

Key: the API Gateway (or a proxy like YARP) routes traffic. You never do a big-bang migration. The monolith shrinks slowly as each capability is extracted.


When NOT to Use Microservices

Be honest about these signals:

  • Team is fewer than 10 engineers β€” operational overhead isn't worth it yet
  • Domain is not well understood β€” premature splitting creates wrong boundaries (a distributed monolith is worse than a monolith)
  • No DevOps capability β€” without Kubernetes, CI/CD pipelines, and observability tooling, microservices are unmanageable
  • No clear scalability need β€” don't optimize for problems you don't have
  • Startup / MVP phase β€” move fast with a monolith; extract services when you understand what needs to scale

The truth: most systems don't need microservices. What they need is a well-organized codebase with clear module boundaries β€” and the option to extract services if the need arises.


Key Takeaways

  • A microservice is independently deployable, owns a single business capability, and has its own database.
  • Teams move to microservices for: team autonomy, independent scaling, tech flexibility, and fault isolation.
  • Real costs: network latency per call, distributed transaction complexity (Saga), massive operational overhead, testing complexity.
  • The distributed systems fallacies will all bite you β€” plan for network failures, not just happy paths.
  • Modular monolith often gives you most of the benefits with far less complexity. Start here.
  • Migrate incrementally using the strangler fig pattern β€” never do a big-bang migration.
  • Don't use microservices if: small team, unknown domain, no DevOps capability, no clear scale need, or MVP stage.

Enjoyed this article?

Explore the System Design learning path for more.

Found this helpful?

Share:𝕏

Leave a comment

Have a question, correction, or just found this helpful? Leave a note below.