The microservices pitch is seductive: independent deployments, team autonomy, technology heterogeneity, targeted scaling. Each service can use the best tool for the job. Teams can deploy without coordinating with anyone else.

These benefits are real. So are the costs that don’t make it into the architecture review slides.

The Tax You Pay for Distribution

When you move from a monolith to microservices, you don’t eliminate complexity - you redistribute it. The logic that was a function call becomes a network call. The transaction that was atomic becomes an eventual consistency problem. The stack trace that showed you exactly what happened becomes a distributed trace spanning six services.

The tax is proportional to the number of services and to how tightly coupled those services are. Organizations that extract microservices well - clear domain boundaries, minimal cross-service coordination - pay a manageable tax. Organizations that extract microservices badly - services that share databases, teams that need to coordinate every deployment - pay a brutal one.

Specific Hidden Costs

Service Discovery and Health Checking

In a monolith, calling a function is O(1). In a microservices architecture, service A needs to know where service B is running, whether it’s healthy, and how to route to it. You need a service registry (Consul, Kubernetes services, AWS Service Discovery), health check endpoints in every service, and logic to handle the case where the registry is wrong.

This is solvable - Kubernetes makes it mostly automatic - but “mostly automatic” still means engineers need to understand it when things go wrong.

Distributed Tracing

When a user request spans 7 services, debugging a latency issue requires tracing the request through all 7. Without distributed tracing infrastructure, you’re correlating logs across services by hand, which is miserable.

Setting up distributed tracing properly requires:

  • A tracing backend (Jaeger, Zipkin, Honeycomb, Datadog APM)
  • Instrumentation in every service (OpenTelemetry)
  • Trace context propagation through every HTTP client and message queue
  • Engineers trained to read distributed traces

The infrastructure costs $100-500/month for a moderate-size deployment. The engineering time to instrument every service properly is 1-2 weeks per service. And traces only help if engineers know how to use them - which requires investment in tooling documentation and training.

Network Failure Handling

In a monolith, function calls don’t fail with connection refused, timeout, or 503 Service Unavailable. In a microservices architecture, every service call can fail for reasons completely unrelated to your code - network blips, the other service is deploying, the other service is at capacity.

Every service client needs:

  • Retry logic with exponential backoff
  • Circuit breakers to stop sending traffic to unhealthy services
  • Timeout handling (and agreed-upon timeouts between teams)
  • Fallback behavior when the service is unavailable

This is 2-4 days of engineering work per service pair. At 20 services with an average of 3 upstream dependencies each, that’s 60 service pairs and several months of engineering time. Libraries help (Resilience4j for Java, go-resilience, Circuit Breaker in Polly for .NET) but the work is real.

Data Consistency Without Transactions

In a monolith with a shared database, a transaction that touches multiple tables is atomic. Service A and service B each have their own database. A business operation that requires updating both - say, creating an order and decrementing inventory - cannot be wrapped in a single transaction.

The solutions (Saga pattern, outbox pattern, eventual consistency with compensating transactions) work. They are also significantly harder to implement correctly and reason about than database transactions.

When you have an inconsistency bug in a microservices architecture, finding it requires understanding the distributed state across multiple services’ databases. When you have an inconsistency bug in a monolith, you read the transaction log.

Issue Monolith Microservices
ACID transactions Native Saga pattern
Debugging latency Stack trace Distributed trace
Data consistency bugs Query one DB Cross-service correlation
Deployment coordination None Service mesh / versioned APIs
Local development One process docker-compose with 12 services

Local Development Complexity

Running a feature that spans 3 services locally requires running 3 services, their dependencies (databases, queues), and having them all configured to talk to each other. This is why docker-compose files with 12 services are common in microservices shops.

The cognitive load of onboarding a new engineer is higher. The time to “first feature contributed” is longer. Engineers need to understand not just their service but the contracts with upstream and downstream dependencies.

Operational Overhead Per Service

Each service needs:

  • A CI/CD pipeline
  • Monitoring and alerting
  • Logging configured and routed
  • A deployment configuration (Kubernetes manifests, Helm chart)
  • A runbook for common failure modes
  • An oncall rotation that understands it

In practice, teams try to templatize this. “We have a service template that includes all of this.” Templates help but don’t eliminate the overhead. At 50 services, maintaining 50 deployment configurations is a real engineering cost.

When Microservices Are Right

The benefits are real for:

  • Organizations with multiple teams that need to deploy independently
  • Services with genuinely different scaling characteristics (video processing vs. API serving)
  • Systems where technology diversity is necessary (ML inference in Python, business logic in Java)
  • Very large codebases where a monolith’s deployment has become a coordination problem

The honest threshold: microservices make sense when the pain of the monolith (long deployments, team coordination overhead, inability to scale specific bottlenecks) exceeds the operational tax of distribution. For most early-stage startups, that threshold is not reached until you have 10+ engineers or specific technical requirements.

The Modular Monolith Alternative

Many teams get the benefits without the full distributed systems tax by building a well-structured monolith: separate modules with clear interfaces, separate CI pipelines for different domains, separate databases per module even in a single process.

A modular monolith can be extracted into services when the need is clear. A distributed system cannot be un-distributed easily.

Bottom Line

Microservices have real benefits and real costs. The benefits - team autonomy, independent scaling, technology flexibility - are genuine and worth pursuing when you have the problems they solve. The costs - distributed tracing, network failure handling, eventual consistency, local development complexity, per-service operational overhead - are also genuine and rarely fully accounted for in architecture decisions. The question to ask is not “are microservices better?” but “are the benefits greater than the operational tax, given our specific team size and technical requirements?” That answer is “no” more often than the pitch deck implies.