Skip to main content

Nano-Services Anti-Pattern

Services too small to be useful, requiring many inter-service calls and creating excessive complexity.

TL;DR

Nano-services are services that are too fine-grained to be useful: single utility functions exposed as microservices. A business operation requires calling 10 nano-services, introducing network latency (100ms+), operational complexity (10 services to manage, monitor, deploy), and fragility (10 failure points). Solution: Services should be coarse-grained, aligned with business capabilities. Start with a monolith; split only when clear scaling benefits emerge.

Learning Objectives

  • Identify nano-services in your architecture
  • Understand the cost of excessive service granularity
  • Align service boundaries with business capabilities (Domain-Driven Design)
  • Decide when to build a service vs. a library
  • Apply the "monolith until it hurts" principle
  • Design services that a single team can own

Motivating Scenario

A company splits its e-commerce platform into 25 microservices: AuthService, ValidationService, LoggingService, EmailService, PaymentService, InvoiceService, ShippingService, ReportingService, CacheService, MetricsService, NotificationService, PricingService, InventoryService, UserService, ProductService, OrderService, ReviewService, SearchService, AnalyticsService, StorageService, ConfigService, MonitoringService, SecurityService, RateLimitingService, and HealthCheckService.

A simple operation—"create order"—requires calling 8 services: Auth → Validation → Order → Pricing → Inventory → Payment → Invoice → Notification. Each call adds 10ms latency; total = 80ms minimum. The company now manages 25 services instead of one, with 25 deployments, 25 monitoring dashboards, 25 potential failure points. Six months later, they realize the system is slower, more fragile, and harder to change than the original monolith.

Core Concepts

The Costs of Fine-Grained Services

Each additional service adds operational burden: deployment pipeline, monitoring, versioning, documentation, team coordination. The benefits (independent scaling, fast deployment) only justify these costs when the service is substantial and used across multiple teams.

Service Granularity Sweet Spot

The right size for a service depends on team ownership, scaling needs, and business domain boundaries. Too fine and you get nano-services. Too coarse and you're back to a monolith.

GranularityService CountCharacteristicsProblem
Monolith1Single codebaseHard to deploy pieces independently; tight coupling
Nano-Services20+Function = service20 deployments, 20 failure points; complex tracing
Right-Sized5-15Business capability per serviceBalances independence with operational simplicity

Practical Example

# Example: Create order in nano-service world
# Client must orchestrate 10 service calls

class OrderClient:
def create_order(self, user_id, items):
# Call 1: Auth Service
user = self.auth_service.get_user(user_id)
if not user:
raise Exception("User not found")

# Call 2: Validation Service
if not self.validation_service.validate_items(items):
raise Exception("Invalid items")

# Call 3: Pricing Service
total = self.pricing_service.calculate_total(items)

# Call 4: Inventory Service
if not self.inventory_service.check_stock(items):
raise Exception("Out of stock")

# Call 5: Payment Service
payment_result = self.payment_service.charge(user.id, total)

# Call 6: Invoice Service
invoice = self.invoice_service.create_invoice(
user.id, items, total, payment_result.id
)

# Call 7: Shipping Service
shipping = self.shipping_service.reserve(user.id, items)

# Call 8: Notification Service
self.notification_service.send_confirmation(user.email, items, total)

# Call 9: Reporting Service
self.reporting_service.log_order(user.id, total)

# Call 10: Analytics Service
self.analytics_service.track_purchase(user.id, total)

return {"order_id": order_id}

# 10 network calls for one business operation
# Each call: 5ms overhead + 10ms service processing = 15ms
# Total: 150ms minimum
# If any service is down: entire operation fails

When to Use / When to Avoid

Nano-Services (Avoid)
  1. Each utility function becomes a service
  2. Validation, logging, email as separate services
  3. Single business operation calls 10+ services
  4. Network overhead dominates (100+ms latency)
  5. 25 services to deploy, monitor, and maintain
  6. Distributed tracing required to debug simple operations
Right-Sized Services (Prefer)
  1. Services align with business capabilities
  2. A team owns 1-2 services (two-pizza rule)
  3. Business operation calls 2-3 services
  4. Network overhead minimal (20-30ms latency)
  5. 5-15 services total (manageable operationally)
  6. Clear ownership and deployment boundaries

Patterns & Pitfalls

Align service boundaries with business subdomains (Orders, Payments, Shipping). Each service represents a coherent business capability, not a technical function.
A service should be small enough for one team (~6-8 people) to understand, own, and deploy independently. If it requires multiple teams to understand, it's probably too large.
Start with a monolith. Only split into services when clear pain emerges: independent scaling needs, deployment conflicts, or team scaling. Don't architect for scale you don't have.
Shared utilities (validation, logging, email) should be libraries (Maven, npm packages), not services. Only extract to a service if it needs independent scaling or deployment.
Avoid creating ValidationService, LoggingService, CachingService. These belong in libraries or in the monolith until specialization is needed.
Each service costs in deployment infrastructure, monitoring, documentation, and team coordination. 25 services costs 10x more to operate than 3.

Design Review Checklist

  • Service represents a complete business capability?
  • Service can be owned/understood by a single team?
  • Service has clear, stable boundaries with other services?
  • Service scales independently from others?
  • Service wouldn't benefit from being a library?
  • Fewer than 3 external service dependencies per operation?
  • Service has multiple callers (not called by just one other service)?
  • Operational burden (monitoring, deployment) justified by benefit?
  • Team has capacity to own and operate this service?
  • No shared infrastructure service (logging, validation, caching)?

Self-Check

  • How many services is too many? Depends on team size and operational maturity. 5-15 services for a typical company. 25+ is a red flag.
  • What's the minimum viable service? Something that a team can own and deploy independently, with clear business value.
  • Should validation be a service? No—validation logic should be in libraries or in the service that uses it. Same for logging and caching.
  • When should I split a monolith? When scaling, deployment, or team size issues emerge. Not preemptively.
  • How do I know if a service is too small? If it's called by just one other service, or if it's purely a technical utility. Consider merging it.

Next Steps

  1. Audit existing services — Count services; identify truly utilitarian ones
  2. Map services to teams — Can each team own its services? If not, too many services
  3. Identify merged services — Which services should be combined based on cohesion?
  4. Plan consolidation — Gradually merge nano-services; don't do all at once
  5. Define future boundaries — Plan services around business capabilities, not technical details
  6. Establish sizing guidelines — Document what minimum viable service size is for your org

References