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:
in a microservicce, you typically have:
and in a broader miniservice:
Instead of 50 small repos for billing, you have:
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


Every now and then, one may wish to have had a simple script to check out or