Skip to main content

Nanoservices

Nanoservices over-decompose a system into services so tiny that network, coordination, and operational overhead exceed their value. Right-size services around business capabilities using DDD, or start with a modular monolith, and consolidate chatty co-changing services.

Nanoservices is the anti-pattern of decomposing a system into services that are too fine-grained — sometimes one service per function or per database table. It is microservices taken past the point of diminishing returns, where each service is so small that the cost of making it a separate deployable (network calls, serialization, deployment, monitoring, on-call) dwarfs the logic it contains. The result is more overhead than value.

Why It Happens

Nanoservices usually stem from misapplying "small is good." Teams hear that microservices should be small and single-purpose and push that to an extreme, equating smaller with better. The single-responsibility principle gets misread at the service level, splitting on technical functions rather than business capabilities. Organizational enthusiasm for microservices, or a desire for each developer to "own" a service, encourages proliferation. Without domain-driven boundaries, services are carved by convenience or by data structure, producing a swarm of tiny components that should have been methods or modules within one service.

Why It Hurts

When logic is spread across many tiny services, a single business operation requires a cascade of network calls between them, turning in-process method calls into distributed transactions. This amplifies latency, multiplies failure modes, and demands resilience machinery (retries, timeouts, circuit breakers) just to do what one function used to do reliably. Operational overhead explodes: each service needs deployment, monitoring, logging, security, and on-call coverage, so the fixed cost per service overwhelms the small benefit each provides. Most of the codebase becomes glue and serialization rather than business logic. Debugging spans many hops, and changes that should be local now require coordinating releases across services.

Warning Signs

  • Services correspond to individual functions or single tables.
  • A typical request fans out into many chained service calls.
  • More code handles serialization and inter-service plumbing than business logic.
  • Each tiny service carries the full operational burden of a real service.
  • Simple changes require coordinated deployments across several services.

Better Alternatives

Aim for right-sized services aligned to business capabilities, not technical functions. Use Domain-Driven Design to draw service boundaries around bounded contexts, so each service owns a cohesive slice of the domain and its data. Consider a modular monolith first: enforce strong internal module boundaries within one deployable, and split out a service only when a clear scaling, ownership, or deployment need justifies the distributed cost.

How to Refactor Out of It

Identify clusters of nanoservices that always change and deploy together or that chat constantly over the network — these belong in one service. Consolidate them, turning cross-network calls back into in-process calls and removing the resilience scaffolding they required. Redraw boundaries along bounded contexts so each service maps to a business capability with its own data. Where the overhead was never justified, collapse multiple services into a modular monolith and split again only with a concrete reason. Measure the result by reduced inter-service traffic, simpler operations, and a higher ratio of business logic to plumbing.