TL;DR: Across 12,000 mapped microservices, 63% had zero production callers. Roughly 7,500 services were running, costing money, consuming engineering attention, and expanding attack surface. For no user, no internal system, and no business reason. The pattern repeats across portfolios. The detection method is simple. The fix follows a predictable cost curve once you commit to it.
Key Takeaways: - 63% of mapped microservices in our client portfolio had no production caller. That is not a one-company anomaly. - "Service is up" in your dashboard is not the same as "service serves anyone." Standard observability masks this completely. - Detection takes 30 days of traffic logs and a service mesh inventory. The hard part is acting on what you find.
63% of 12,000 Microservices Had No Production Caller

We mapped 12,000 microservices across our client portfolio. 63% had no production caller. Roughly 7,500 services were running, burning money, consuming engineering attention, and creating compliance surface area. For absolutely nothing. The worst part? Nobody on those teams knew.
"No production caller" means a service that isn't invoked by any other service or end-user flow in production. It might respond to health checks. It might pass readiness probes. It might even get pings from synthetic monitors. But no real workload talks to it. It is a ghost in your service mesh.
Every one of those 7,500 services still costs something. It consumes compute, even if minimal. It burns through monitoring and logging volume. It requires security patching.
It shows up in audit logs. It occupies on-call mental bandwidth. This pattern recurs at every scale of architecture, from startups to enterprises pushing past 1,000 services.
The shock isn't that dead services exist. The shock is that the teams running them had no idea. Their dashboards showed green. Their SLOs were met.
Their cost reports were buried under bigger line items. The original case study that surfaced this pattern walked through one team's 15-to-3 collapse, the same phenomenon at smaller scale.
But if 7,500 services were clearly dead, why had nobody deleted them? That's the harder question. It's the one that actually explains how sprawl happens.
Why Smart Teams Accumulate Dead Services (And Never Notice)
Deletion has a political cost. Nobody wants to be the engineer who kills a service another team built, especially when ownership is ambiguous. The "who owns this?" question becomes a shield: "Better not touch it until we find out."
Next, the sunk-cost trap. "We spent three months building this, surely someone will use it." That sentence has justified more unused infrastructure than any technical debt write-off.
The original investment feels too large to write off, even when the service has produced zero value since launch.
Then comes the monitoring gap. Your dashboards show "service is up." They almost never show "service is serving anyone." Green status masks zero value.
On-call engineers learn to trust the green. The alternative, questioning every service, is too expensive to do continuously.
Finally, the "we might need it someday" defense. This is the technical version of hoarding, rationalized as future flexibility. It survives because nobody can prove a negative: "We didn't need it" sounds like "We didn't need it yet."
If the problem is invisible to standard monitoring, how did we actually find the 7,500 dead services? The method turned out to be embarrassingly simple.
The Detection Method That Found 7,500 Dead Services
Detection is five steps. None of them require new tooling.
Step 1: Enumerate every service in your service mesh or API gateway. This is your denominator. Istio, Linkerd, Kong, AWS App Mesh, or your API gateway's service registry all maintain this list.
If a service isn't in the mesh, it shouldn't be in production.
Step 2: Pull production traffic logs over a 30-day window. Datadog, New Relic, Prometheus, or CloudWatch all keep this. You're looking for invocation counts per service, filtered to non-synthetic traffic.
Step 3: Filter for services with zero invocations from other services AND zero end-user traffic. Exclude health checks, readiness probes, and synthetic monitors. What remains is your dead-service candidate list.
Step 4: Cross-reference with git activity. Services with no commits in 6+ months and no production callers are almost certainly dead. This catches long-dormant services that pre-date your current observability stack.
Step 5: Validate with a 2-week shadow period. Tag suspect services in your observability stack, watch for any traffic spike, and confirm zero organic calls before deletion. This protects you from seasonal workloads or batch jobs that fire monthly.
Why this beats dependency-mapping tools: those tools show you what calls what, but not whether the caller is itself in production use. You can map a chain of 10 services and find that none of them are reached by anything real.
The Spring Boot production checklist includes a section on service lifecycle that maps to these steps. Run detection quarterly, not just at incident time.
Detection is the easy part. The harder question is: once you know a service is dead, what does consolidation actually look like, and what does it cost before it starts saving money?
The 15-to-3 Consolidation: Real Cost Math, Not Vibes

One team we worked with was running 15 microservices at $3,110/month total. That's $37,320/year for a system that could have been three services. After the detection audit, 12 services had no production caller.
Three remained: a payment service, an auth service, and a notification worker. Here is the new monthly run-rate: - Kubernetes cluster: $220/month - Three services × three pods average: $280/month - Three RDS databases: $360/month - Redis cache: $45/month - ALB: $25/month - CloudWatch: $45/month - Total: $975/month
The savings: $2,135/month, $25,620/year. And that's before counting engineering hours reclaimed from maintaining dead services.
But the honest cost of getting there matters too. This team spent 8 months in architectural debate and $22,000 in wasted infrastructure spend before admitting "maybe we over-engineered this."
Eight months of "but what if we need it?" and "let's just keep it for now." Eight months of monitoring, patching, and on-call rotation for services no one used.
During the actual migration, the Spring Boot Troubleshooting Cheatsheet stayed open on a second monitor. Consolidating services surfaces dependency bugs that were previously hidden behind network boundaries.
When two services that "talked via API" become two modules in one service, every implicit contract becomes explicit, sometimes painfully so.
When a production incident hit during the migration window, the Production Engineering Cheatsheet served as the reference. Migration windows are when incident response gets brittle.
Having a checklist for "what to check first" kept the team from making panic-driven decisions.
The numbers tell a clean story in hindsight. But during the consolidation, every team member kept asking the same question: "How do I know this new merged service won't become the next monolith we have to split?" Here is the test we used.
The Function-vs-Service Test: When to Keep What You Have
Not every service is a candidate for merging. Some genuinely need to stay independent. The question is how to tell the difference.
Test 1: Independent deploy cadence. If a service hasn't been deployed independently in 90 days, it's a function, not a service. Microservices earn their keep by enabling independent releases. No independent release means no service.
Test 2: Independent scaling needs. If a service runs at the same scale as its siblings, it doesn't need its own pod, database, or load balancer. Auto-scaling, dedicated node pools, and per-service quotas are operational overhead. Pay for them only when you need them.
Test 3: Different team ownership. If one team owns all the "microservices," they're modules, not services. Microservices are an organizational pattern as much as a technical one. Same team, same repo, same on-call rotation? That's a modular monolith.
Test 4: Failure isolation value. If killing this service doesn't protect the rest of the system, the isolation cost is pure overhead. True failure isolation means a crash here doesn't cascade there. Most services don't have this.
Test 5: Network boundary cost. If two services share a database, share a deployment pipeline, and live in the same repo, the "microservice" label is decorative. The network hop adds latency and failure modes for no benefit.
Decision rule: if a service fails 3 or more of these 5 tests, merge it. If it passes all 5, keep it. Audit the ones that stay every six months.
The original 15-microservice teardown walks through how this test played out on a real system.
The 63% finding isn't a one-time disaster to clean up. It's a recurring tax that reappears every time you skip an architecture review. What does a review process that prevents it actually look like?
What the 63% Means for Your Architecture Review Process
The fix is organizational, not just technical.
Make "production caller count" a required field in every service's runbook. If you can't name the caller, the service shouldn't ship.
This sounds obvious. In practice, almost no team enforces it.
Run a sprawl audit quarterly, not just at incident time. The 7,500 dead services we found didn't appear overnight.
They accreted over years of "let's add this small service" without a matching "let's retire that old one." A quarterly audit catches drift before it becomes a budget problem.
Reward deletion the same way you reward creation. If engineers get credit for shipping new services, they should get equal credit for retiring unused ones.
Most organizations track new services shipped. Almost none track services retired. That asymmetry is why deletion never happens.
The teams that prevent sprawl aren't the ones with the strictest architecture boards. They are the ones who measure service utility, not just service count. They ask "what does this service do for users?" not "how many services do we run?"
The real outcome: when you cut dead services, you don't just save $25,620/year. You reclaim the cognitive load of an engineer who no longer has to mentally model a service that serves no one. That's the line item that doesn't show up in any cloud bill.
For teams running production microservices at scale, the production engineering cheatsheets include detection scripts and audit templates that run in under an afternoon.
Frequently Asked Questions
Q: How do you detect microservices with no production caller?
A: Pull 30 days of production traffic logs from your observability stack. Enumerate every service in your service mesh or API gateway. Then filter for services with zero invocations from other services and zero end-user traffic.
Cross-reference with 6+ months of git inactivity to catch long-dormant services. The detection method is simple. It is the willingness to act on the result that most teams lack.
Q: What percentage of microservices are typically unused?
A: In our mapping of 12,000 microservices, 63% had no production caller. While every codebase differs, the pattern is consistent: services accumulate faster than they're retired, especially in organizations where deletion carries political cost. A quarterly sprawl audit is the only reliable way to know your own number.
Q: When should you NOT consolidate microservices?
A: Don't consolidate services that pass all five tests in the function-vs-service framework. The tests cover independent deploy cadence, independent scaling needs, different team ownership, failure isolation value, and a real network boundary.
Consolidation saves money on dead services. It creates technical debt on services that actually need isolation.
Q: How long does microservice consolidation take?
A: In the 15-to-3 case study, the team spent 8 months debating before consolidation. The detection and decision phase dominates the timeline, not the code migration. Budget 2-3 months for a portfolio-wide audit if you have more than 50 services.
Q: What's a healthy ratio of microservices to engineering team size?
A: There's no universal ratio, but a useful heuristic: if you have more microservices than engineers can confidently name their purpose for, you have sprawl. The 63% finding suggests most organizations are running far more services than their actual needs require. The right measure is services-per-team with clear ownership, not total service count.
About the author
Mayank Singh is a software developer at Levitation Infotech, where he builds web and AI-powered applications across the company’s fintech, healthcare, and enterprise projects.
