Microservices Anti patterns

Microservices Anti Patterns

Microservices Anti-Patterns - What NOT to Do

Microservices Anti-Patterns

What NOT to do when building microservices - Common mistakes and how to avoid them

Quick Summary (30 seconds):
The most dangerous anti-patterns: Shared Database, Distributed Monolith, Nano-services (too fine-grained), Sync by Default, No Circuit Breaker, Ignoring Fallbacks, No Observability. Avoid these to prevent distributed system chaos.

Anti-Pattern 1: Shared Database

The Problem
Order Service ----\
                   |--- Single Database
Payment Service ---/
Symptoms:
  • Services cannot scale independently
  • Schema changes affect all services
  • Single point of failure (database goes down = everything down)
  • Cannot choose different database technologies
  • Tight coupling between services
Solution:
  • Database per Service pattern
  • Use events for data synchronization
  • Each service owns its data
  • Services communicate via APIs, not direct database access

Anti-Pattern 2: Distributed Monolith

The Problem

Services are split technically, not by business capability. Deployed separately but must be upgraded together.

Synchronous call chain:
Service A -> Service B -> Service C -> Service D -> Service E

Symptoms:
- Changing one service requires changing 5 others
- Cannot deploy independently
- High network latency (worst of both worlds)
Symptoms:
  • Multiple services must be deployed together
  • Services are not independently deployable
  • Long synchronous call chains
  • Network latency + coordinated deployments = nightmare
Solution:
  • Proper bounded contexts using DDD
  • Use async communication where possible
  • Decompose by business capability, not technical layers
  • Ensure each service can be deployed independently

Anti-Pattern 3: Nano-services (Too Fine-Grained)

The Problem

Creating too many tiny services - each with only 2-3 endpoints.

Example: 200 services for a simple e-commerce site
Single request calls 15 different services
Network overhead: 15 x (latency + serialization)
Symptoms:
  • High network overhead
  • Increased latency (each call adds 10-50ms)
  • Complex debugging across dozens of services
  • Difficult to maintain data consistency
  • Operations nightmare
Solution:
  • Find the right service size - not too big, not too small
  • Start with larger services, split when needed
  • Rule of thumb: a team of 3-6 developers per service
  • Each service should have meaningful business value

Anti-Pattern 4: Sync by Default

The Problem

Using synchronous HTTP calls for everything, even when async would work better.

Service A --HTTP--> Service B --HTTP--> Service C
     |                |
     v                v
Service D        Service E

Total response time = sum of all service times
Symptoms:
  • High latency accumulating across service chain
  • Tight coupling between services
  • Cascading failures (one slow service slows everything)
  • Poor scalability
Solution:
  • Use async messaging for non-critical operations
  • Event-driven architecture
  • Message queues (Kafka, RabbitMQ, SQS)
  • Only use sync when immediate response is needed

Anti-Pattern 5: No Circuit Breaker

The Problem

Not implementing circuit breakers for service-to-service calls.

Payment Service fails (slow)
    -> Order Service waits for timeout (30 seconds)
        -> All threads blocked
            -> Order Service fails
                -> API Gateway waits
                    -> All users see errors
Symptoms:
  • Cascading failures across services
  • Thread pool exhaustion
  • Slow degradation instead of fast failure
  • Entire system can go down from one bad service
Solution:
  • Implement Circuit Breaker pattern
  • Use libraries: Hystrix, Resilience4j, Polly
  • Configure timeouts and fallbacks
  • Monitor circuit breaker states

Anti-Pattern 6: Ignoring Fallbacks

The Problem

No plan for when a service call fails - user just sees an error.

Bad approach:
Payment fails -> Order fails -> User sees "Error 500"

Better approach:
Payment fails -> Mark order as "Payment Pending"
                -> Notify user, retry later
                -> Show cached/partial data
Symptoms:
  • Poor user experience
  • Complete failure instead of graceful degradation
  • No retry mechanism for transient failures
Solution:
  • Always provide fallback responses
  • Implement retry with backoff
  • Cache data when possible
  • Design for partial availability

Anti-Pattern 7: No Observability

The Problem

Deploying microservices without proper monitoring, logging, and tracing.

Monolith debugging:
grep error.log

Microservices debugging (without observability):
Search 50 services x 10 instances
Different log formats
No request tracing
Symptoms:
  • Cannot debug production issues
  • No visibility into service dependencies
  • Don't know which service is slow or failing
  • Blind deployments
Solution (the three pillars):
  • Logging: Centralized log aggregation (ELK, Loki)
  • Metrics: Prometheus + Grafana
  • Tracing: Distributed tracing (Jaeger, Zipkin)
  • Health checks and readiness probes

Anti-Pattern 8: Versioning Ignorance

The Problem

Making breaking API changes without versioning strategy.

Problem:
Service A uses API v1 of Service B
Service C uses API v2 of Service B
Cannot upgrade all services simultaneously

Result: Coordinated deployments or breaking changes
Symptoms:
  • Cannot deploy services independently
  • Breaking changes cause production failures
  • Forced coordinated upgrades
Solution:
  • API versioning in URL (/v1/resource, /v2/resource)
  • Consumer-driven contracts (Pact)
  • Keep backward compatibility for at least one version
  • Deprecation period with clear communication

Anti-Pattern 9: No Idempotency

The Problem

APIs that are not idempotent, causing duplicate operations on retry.

Without idempotency:
User clicks "Pay" -> Network timeout -> User clicks again
Result: User charged TWICE

With idempotency:
Each request has unique id -> Server processes once
Result: User charged once
Symptoms:
  • Duplicate charges, orders, or records
  • Data inconsistency after retries
  • Cannot safely retry failed operations
Solution:
  • Idempotency keys for all write operations
  • Client generates unique request ID
  • Server stores processed IDs in database
  • Safe retry mechanism

Anti-Pattern 10: No Platform Team

The Problem

Each service team builds their own infrastructure (service discovery, logging, tracing).

Symptoms:
  • Inconsistent implementations across teams
  • Reinventing the wheel for each service
  • High cognitive load on developers
  • Cannot debug across services
Solution:
  • Dedicated platform team for infrastructure
  • Provide standardized frameworks and libraries
  • Service mesh (Istio, Linkerd) for cross-cutting concerns
  • Internal developer platform

Quick Reference: Anti-Patterns Summary

Anti-Pattern Problem Solution
Shared DatabaseTight coupling, no independenceDatabase per Service
Distributed MonolithCoordinated deployments + network latencyProper bounded contexts
Nano-servicesToo many services, high overheadFind right service size
Sync by DefaultHigh latency, cascading failuresUse async where possible
No Circuit BreakerCascading failuresImplement circuit breaker
Ignoring FallbacksPoor user experienceAlways provide fallbacks
No ObservabilityCannot debug or monitorLogging, Metrics, Tracing
Versioning IgnoranceBreaking changes cause failuresAPI versioning + contracts
No IdempotencyDuplicate operations on retryIdempotency keys
No Platform TeamInconsistent infrastructureDedicated platform team
The Worst Anti-Pattern of All: Premature Microservices

"Starting with microservices when you don't need them. The cost, complexity, and operational overhead are massive. Start with a well-structured monolith and extract services only when you feel the pain that microservices solve. Premature microservices are worse than no microservices."
Self-Assessment Checklist:
[ ] Do services share a database? (Shared Database anti-pattern)
[ ] Do you need to deploy multiple services together? (Distributed Monolith)
[ ] Do you have more than 5 services per developer? (Nano-services)
[ ] Are all calls synchronous? (Sync by Default)
[ ] Do you have circuit breakers implemented? (No Circuit Breaker)
[ ] Do all API calls have fallbacks? (Ignoring Fallbacks)
[ ] Do you have centralized logging and tracing? (No Observability)
[ ] Do you use idempotency keys? (No Idempotency)
Expert Insight:
"The biggest anti-pattern is the distributed monolith. You get the worst of both worlds: network latency AND coordinated deployments. If your 'microservices' share a database, you don't have microservices - you have a distributed monolith with extra network calls."
Another Critical Insight:
"Fallbacks are not optional. Every service dependency must answer: 'What happens when this call fails?' If you can't answer that question, you're not production-ready. A system without fallbacks is a house of cards."

Key Takeaways

  • Shared Database is the #1 anti-pattern - Database per Service is mandatory
  • Distributed Monolith gives you the worst of both worlds
  • Too small services (nano-services) create more problems than they solve
  • Circuit breakers and fallbacks are not optional - they are mandatory
  • Observability (logs, metrics, traces) must be built from day one
  • Idempotency is required for all write operations
  • Don't start with microservices unless you have a proven need
  • If services share a database, you don't have microservices
Final Advice:
"Start with a monolith. Seriously. Extract services only when you have proven need - different scaling requirements, different teams, or different tech stacks. When you do migrate, avoid these anti-patterns at all costs. They have destroyed many microservices projects before they even had a chance to succeed."
Quick Reference Card:
Aspect              Pro                           Con                       Anti-Pattern
Deployment          Independent                   Complex orchestration     Distributed monolith
Data                Technology choice             Consistency challenges    Shared database
Scale               Elastic per service           Network overhead          Nano-services
Resilience          Fault isolation               Partial failures          No circuit breaker
Performance         Parallel processing           Network latency           Sync by default
Debugging           Small codebase                Distributed traces        No observability