Database Connection Pools Aren't Magic: Why Your App Dies When All Connections Are Exhausted
You added a connection pool. The database queries got faster. Problem solved. Except the pool is a f...
Part 1 of 1
Devesh Korde
September 12, 2026
I watched a company spend six months breaking their monolith into microservices.
The architecture looked beautiful on the whiteboard. Twenty services. Each with its own database. Each with its own CI/CD pipeline. Each supposedly independent.
In production, after the first scaling test, everything broke. A search service got slow. Users waiting for search results timed out. Those timeouts propagated to the recommendation service. The recommendation service timed out. The timeout propagated to the user service. The user service crashed.
One slow service crashed the entire system.
They had a microservices architecture. The system failed like a monolith. Worse: it failed in ways a monolith never would.
The problem was not the microservices. The problem was that they had created a distributed monolith. All the downsides of microservices. None of the upsides.
A monolith is one codebase. One deployment. One database. One process.
The problem with a monolith is clear: if you change one line in one service, you redeploy the entire application. If one service crashes, everything crashes. Code changes in service A can break service B. Everything is tightly coupled through shared code.
The solution seems obvious: break it into microservices.
Microservices promise:
All of this is true. In theory.
In practice, you get a distributed monolith. Here is why.
The Coupling Moved
You removed code-level coupling. But you introduced network-level coupling.
Service A calls Service B. Service B calls Service C. They are still tightly coupled. The coupling is just at the network level instead of the code level.
When Service B is slow, Service A waits. Service A becomes slow. Service A's clients wait. The slowness propagates.
You have not decoupled anything. You have just made the coupling invisible and harder to debug.
The Failures Are Harder
In a monolith, if a service crashes, you get a crash. Everything stops. It is clear something went wrong.
In a distributed monolith, failures are subtle. Service B is slow. Service A times out. Service A retries. Service A times out again. Service A's circuit breaker opens. Service A returns errors. Clients see errors from Service A.
Did Service A fail? No. Service B failed. But the error originated in Service A. The failure cascaded.
A developer looking at Service A logs sees errors. They have no idea the real problem is Service B.
The Operational Complexity Is Exponential
A monolith needs:
A microservices architecture needs:
The operational complexity is not 20x (20 services vs. 1). It is exponential. Because every service depends on every other service, and failures in one cascade to all.
Example: The Cascading Failure
You have 10 services. A deploy goes wrong in one service. The service starts returning errors. It is only 10% of your traffic.
Here is what happens:
One service returning 10% errors cascades into 90% of the system being unavailable.
In a monolith, one deploy rolls back. One fix. Problem solved.
In a distributed monolith, you have to:
The failure was simple. The fix is complex.
Most companies break their monolith into microservices for the wrong reason.
Reason 1: Scalability Theater
"Our monolith does not scale. We need microservices so we can scale individual services."
This is true. But scaling a monolith is easier than managing a distributed monolith.
Before microservices: scale the monolith by adding more instances. Load balancer distributes traffic. Done.
After microservices: scale Service A by adding more instances. Now manage inter-service communication. Now debug cross-service failures. Now monitor distributed tracing.
The scalability problem was real. The solution created worse problems.
Reason 2: Deployment Fear
"We want to deploy Service A without deploying Service B."
This is a real need. But it does not require microservices. Modular architecture with clearly defined interfaces solves this.
Deploy modules instead of services. Same benefits. None of the distributed complexity.
Reason 3: Team Organization
"We have 10 teams. Each team should own a service."
This is the worst reason. Because now you have 10 teams coordinating across network calls instead of coordinating through code reviews.
Team A makes a change to the API. Team B did not update their client code. Failure at runtime.
You have not reduced coordination. You have just moved it from the codebase to the network.
Reason 4: Hype
Microservices are popular. Netflix uses them. Uber uses them. Amazon uses them.
What they do not tell you: Netflix, Uber, and Amazon have entire teams dedicated to making microservices work. They have built distributed systems infrastructure that most companies do not have.
They have observability tools. Circuit breakers. Service meshes. Chaos engineering. Years of experience.
Most companies shipping microservices have none of this.
Microservices are not bad. They are just not a universal solution.
The tradeoff is:
Monolith:
Microservices:
The question is: what problem are you actually solving?
If the problem is "our codebase is too big to understand," microservices does not solve it. You still have a big codebase, now distributed across 20 services.
If the problem is "we want independent deployment," you can solve it with modular architecture and clear interfaces.
If the problem is "our monolith does not scale," you can solve it by scaling the monolith. Most monoliths can handle 10x more load just by adding more instances.
If the problem is "we want different teams working independently," microservices creates new problems: coordinating across network calls is harder than coordinating through code.
Microservices make sense when:
- They have different data models, different business logic, different scaling requirements
- They do not need to coordinate
- Service B is fine at 1,000 requests/second
- Scaling them independently saves money and complexity
- Service B works fine in Python
- The language mismatch is a blocker for a monolith
- Service B changes monthly
- Redeploying Service B every time Service A changes is wasteful
If none of these are true, you do not need microservices. You need better code organization.
Most companies using microservices are experiencing:
And they are not getting:
They built a distributed monolith. They got the downsides of both architectures.
Step 1: Be honest about your problem
Do you have a scalability problem? Or a code organization problem? They require different solutions.
Step 2: Try modular architecture first
Break your codebase into modules. Each module has clear boundaries. Each module has its own database schema (in the same database). Each module can be deployed independently (with careful coordination).
You get decoupling without distribution.
Step 3: Only use microservices when necessary
Once you have proven that modularity does not scale, then consider microservices. Not before.
Step 4: Build operational maturity
If you use microservices, invest in the infrastructure: distributed tracing, circuit breakers, service mesh, chaos engineering, chaos testing.
Do not ship microservices without this. You will regret it.
The microservices architecture diagram shows 20 boxes, each independent.
The reality is 20 boxes with hundreds of arrows between them. Each arrow is a dependency. Each dependency is a failure point.
Do not optimize for the diagram. Optimize for the reality.
Microservices are not bad. But they are not a silver bullet. They solve specific problems at the cost of introducing new problems.
Most companies do not have the specific problems that microservices solve. They ship microservices anyway and get a distributed monolith.
Be honest about what problem you are solving. Then pick the architecture that solves that problem without creating new ones.
Most of the time, that architecture is not microservices.