Posted on

When Microservices Go Rogue

Thoughts of the Fractional Chief

How “Nanoservices” Created 500+ Repos
… and why to small will cost you… 
TL;DR

Microservices can help teams move faster—until they explode into nanoservices: tiny, single-endpoint codebases scattered across hundreds of repositories with no boundaries, no cohesion, and no architectural sense. One service per path or method is not microservice architecture. It’s fragmentation.
The escape hatch is not returning to a monolith, nor is it doubling down on microservices. The answer is miniservices: A meaningful middle-path, based on domain-sized, cohesive components that are small enough to understand but large enough to contain meaningful behavior, and which can be split in a reasonable way, should there be a need. “Miniservices” restore control, reduce cognitive load, and prevent microservice spraw. 


1. The Accident Nobody Planned: Microservices Turning Into Nanoservices

This problem always begins with good intentions.

A team wants:

  • Autonomy
  • Scalability
  • Isolation
  • Independent deployment
  • Faster iteration

So they break the system into microservices.

But without boundaries, ownership, or domain thinking, teams quickly slide into:

“One endpoint = one service.”

Before long, the architecture contains:

  • 500+ Git repositories
  • Services with 50 lines of code
  • Duplicated logic (Copy – paste, no shared libraries)
  • Inconsistent conventions
  • Circular dependencies via HTTP
  • Non-stop CI/CD pipelines
  • No unified view of the system
  • No stable contracts
  • Dozens of small deployment failures per week
  • Extreme costs of Lambda functions or operation via containers, when taken to the extreme.

This is not distributed architecture – this is distributed confusion.


2. Why Nanoservices Form: The Root Causes

1. No shared definition of “microservice”

One team treats a microservice as a meaningful domain boundary.

Another treats it as “a new repo for every small piece of logic.”

2. Over-focus on independence

Teams think independence means “split everything” instead of “separate things that change for different reasons.”

3. Fear of merge conflicts or shared ownership

Avoiding coordination by dividing code endlessly is attractive—until everything depends on everything else.

4. CI/CD enthusiasm without restraint

“If it can be deployed separately, it should be deployed separately” leads to chaos.

5. No domain model

Without a clear domain understanding, boundaries follow endpoints—never business logic.

The result:

a fractal explosion of tiny services that nobody understands or controls.

This is just uncontrolled, unimpeded software design without architecture, which leads to a truly unmaintainable mess, that will not just cost you down the line, it will also cost a lot to run it, as it will need one lambda or container per function, and this leads to a massive overhead in the infrastructure, that will cost you as well, as you will have to oversize the infrastructure to cater for the additional overhead. 


3. The Cost: When Services Become Too Small to Be Useful

Nanoservices introduce friction and costs everywhere:

1. Cognitive overload

Developers must understand dozens of repos to make one change.

2. Slow delivery

Every feature requires touching N services, coordinating deployments, and updating contracts.

3. Performance issues

Network latency and cross-service chatter balloon.

4. Operational drag

Monitoring, alerting, dashboards, pipelines—multiplied by hundreds.

5. Versioning hell

Breaking changes ripple through the system like dominoes.

6. Hard-to-find bugs

Failures disappear into the cracks between services.

7. Ownership confusion

Who owns what? Nobody knows.
Or everybody does – which is worse.

8. Maintenance overhead

Since there is often a lot of copy-paste code in such constructs, especially if combined with a specific lack of shared libraries, you will have to fix bugs and security issues, repeatedly.

In short: The “architecture” stops being distributed logic and becomes a massive distributed pain.


4. The False Choice: Monolith or Microservices

When nanoservice chaos becomes obvious, teams usually debate two extremes:

“Should we return to a monolith?”

Pros: simpler, cohesive, easier to understand.

Cons: hard to scale organizationally, risky to evolve if already unstable.

“Should we try to fix the microservices?”

Pros: autonomy, scalability, clear boundaries (in theory).

Cons: the current boundaries are wrong and require a rewrite anyway.

Both extremes are reactionary.

There is a middle ground..
Let’s fix this, calmly and sensibly. 


5. The Better Path: Miniservices

A miniservice is:

  • Organized around a real functional and primarily self-contained domain (think “service”, like “users”, “payment”, “cart”, …).
  • Large enough to contain meaningful logic
  • Small enough to understand
  • Owned by a team
  • Independently deployable
  • Internally cohesive
  • Externally simple 
  • … and, if there are performance or scaling issues, it can relatively easily be split or rewritten, compared to a monolith. 

Think of them as:

“Microservices that actually make sense.”

Not one service per endpoint.

Not one giant ball of everything.

Instead:

  • One domain = one cohesive service (user, wallet, cart, ..)
  • A handful of boundaries instead of hundreds
  • Shared common code and functions  that makes sense. 
  • Stable, well-defined contracts
  • Clear ownership
  • Fewer repos
  • Fewer deployments
  • Fewer failures
  • Easier onboarding
  • Easier refactoring

Miniservices operate as building blocks—not confetti.


6. What Miniservices Look Like in Practice

Each miniservice contains:

  • Its domain logic
  • Its data store (optional but common)
  • Internal modules, not external services
  • Coherent APIs
  • Shared patterns within the domain
  • Consistent error handling
  • Meaningful boundaries

Examples:

Instead of nanoservices like:

user-auth-post/
user-auth-get/
user-session-put/
user-session-delete/


in a microservicce, you typically have:

auth-service/
session-service/

 

and in a broader miniservice:

Instead of 50 small repos for billing, you have:

billing-service/

That includes:

  • invoicing
  • tax rules
  • charge retries
  • refunds
  • billing events

Not because it’s “big”—but because these things belong together, and that it makes perfect sense to group these together in terms of business logic. 


7. The Benefits of Miniservices Over Nanoservices

1. Drastically reduced operational overhead

Monitoring 20 services is hard.

Monitoring 200 is impossible.
Monitoring 500+ – forget it. 

2. Fewer deployments, fewer failures

Bigger services = fewer moving parts = fewer incidents.
When you build a mini service, the moving parts has been integrated tighter directly in the code and not as an external service which means that development  and testing of the integrations and function works in a wholly different way, and you do not run the same risk of introducing bugs by external changes in a foreign api endpoint. 

3. Clearer domain ownership

Teams know exactly what they own and why, and what is in the module/service, and the function of it. 

4. Easier onboarding

New developers learn domains, not random endpoints.

5. More stable contracts

Domains change less frequently than paths and endpoints.

6. Faster delivery

Fewer cross-service dependencies mean less coordination.

7. Real autonomy

Teams can make changes within their domain without touching external services every time.

Miniservices preserve the benefits of microservices without the pathological fragmentation, all while making the domain and function clearer and it’s use more intuitive.


8. A Warning: “Miniservice” Doesn’t Mean “Mini Monolith”

Miniservices are not monoliths in disguise.

They still follow good distributed design:

  • Independent deployability (you redeploy the “user” service etc)
  • Clear domain boundaries (you don’t mix the user and payment services)
  • No shared mutable state
  • Well-defined and documented APIs – use formats like OpenAPI and tools like Swagger/ApiDog to design, generate and test. 
  • Decoupled schemas
  • Versioned contracts
  • No hidden cross-service calls. 

The difference is size and cohesion—not looseness.


9. How to Transition From Nanoservices to Miniservices

1. Identify domains, not endpoints

Look for natural groupings:

  • Billing
  • Authentication
  • Search
  • Cart
  • Profile
  • Notifications
  • Inventory
  • Recommendations

2. Collapse nanoservices into cohesive units

Merge them based on logic, not repo history.

3. Introduce clear domain ownership

One team per domain. No exceptions (well, maybe in small teams – there’s always an exception to the rule).

4. Reduce inter-service chatter

Replace many small calls with internal modules.

5. Establish API contracts that reflect domain responsibilities

Stop mapping endpoints one-to-one.

6. Adopt internal libraries instead of isolated repos

Shared logic doesn’t require a new service.
You could use a shared repo that defines commonly used things such as the database connectivity functions, reading of config files, logging, and other things commonly re-used but rarely changed. 

7. Avoid premature splitting in the future

Split only when two domains evolve independently – be clear about why the split is needed and why you do it.


10. Closing: The Problem Wasn’t Microservices — It Was the Lack of Domain Thinking

Microservices didn’t fail.

The team never defined what a service was, and went to town with the notion that everything is a service, even when it is not.

Nanoservices are what happens when:

  • Endpoints become architectural boundaries
  • Repos become the default unit of structure
  • Autonomy is mistaken for fragmentation
  • Teams split before they understand the domain

The solution is not to swing back to monoliths.

The solution is to adopt sensible, stable, cohesive miniservices.

Miniservices give teams:

  • Autonomy without chaos
  • Flexibility without fragmentation
  • Scalability without sprawl
  • Clarity without over-simplifying
  • Boundaries without bottlenecks
  • Funtional definition and confinement

The solution is not to swing back to monoliths.
The solution is to adopt sensible, stable, cohesive miniservices.

So how do we define a “miniservice”? 

The “miniservice” is a sensible and practical middle-ground approach between the often-unscalable monolith and the pathological microservice hell (nanoservices). It is not a hard definition of size, but a set of logical and practical principles:

  • Logical Cohesion:
    It is a logical and practical grouping of components that belong together, based on a cohesive functional domain (e.g., Billing, Cart).
  • Understandable Scope:
    It isolates business logic into a size that can be easily understood, maintained, and worked on by a single small team.
  • Architectural Flexibility:
    It maintains the ability to be independently deployed and can be split reasonably if independent evolution is genuinely required later.
  • Optimized for Cost and Performance:
    It reduces the massive operational overhead and cross-service communication costs associated with nanoservices while retaining the ability to scale horizontally or vertically more easily than a monolith.

The architecture becomes something everyone can understand—and something the company can grow on, with less costs. 

 (C) (BY) EmberLabs® / Chris Sprucefield