The message arrives from product with an exclamation point: the new feature launches in three weeks, marketing is behind it, and checkout-service should expect 3x traffic. Can infrastructure handle it?
Everyone looks at checkout-service. Its autoscaling is tested, its error budget is healthy, its dashboard is a wall of green. “We’re good,” someone says.
You are probably not good. And the reason has nothing to do with checkout-service.
The bottleneck is never the headline service
The service in the launch announcement is the service that got attention: load-tested, autoscaled, dashboarded, right-sized. Attention is exactly why it will survive. Capacity incidents come from the places attention never reached, and those places sit downstream, in the dependency chain the headline service drags behind it.
The repeat offenders are boringly consistent:
- Database connection pools. The app tier autoscales from 10 instances to 30; each instance opens its pool; the database’s max connections was set in 2024 and does not autoscale with the group. The math crosses somewhere around 2x, and everything upstream of the database experiences it as mysterious latency.
- Shared caches. The Redis cluster that checkout shares with three other services has memory and connection ceilings that were fine at 1x. At 3x, evictions spike and every co-tenant service degrades at once, which looks like four incidents instead of one.
- NAT gateways and egress paths. Every additional instance pushes more traffic through the same network chokepoints. Throughput caps and port exhaustion arrive precisely when everything else is scaling correctly.
- Third-party rate limits. The payment processor, the tax API, the email provider: their limits are in a contract, not in your console, and they do not know about your launch.
- Account-level quotas. Instance count limits, API request rates, IP address pools. Invisible until the autoscaler asks for capacity and the cloud says no.
Notice the pattern: autoscaling does not remove the problem; it relocates it. Scaling the app tier multiplies the pressure on everything behind the app tier. The service survives, and its dependencies inherit the spike.
Capacity planning is a graph traversal with arithmetic
The correct method is almost embarrassingly mechanical. Start at the service receiving the traffic. Walk every hop downstream: services, databases, caches, queues, gateways, third-party APIs, including hops that pass through other services. At each component, attach its actual configured limit, not the folklore version. Multiply the projected load through each hop. The components whose limits are crossed first, and at what multiplier, are your answer: “orders-db saturates at 2.1x, the NAT gateway at 2.6x, everything else clears 3x.”
Two hard parts hide in that paragraph. The first is that the dependency map has to be true, and in most organizations the true map lives in fragments across Terraform, consoles, and the memory of whoever built each piece. The second is that “actual configured limit” requires checking live configuration, not design docs.
Both hard parts are context problems, which is why we lean on the Cloud Intelligence Graph™ for this. Oscar already maintains the dependency structure and can read the live limits, so “product expects 3x traffic to checkout-service, what breaks first?” becomes a traversal it runs in minutes: the ordered bottleneck list, with the configured limit and the crossing point for each, before anyone spends a day wiring up a load test.
Load tests validate the map. They do not replace it.
None of this argues against load testing; it argues about sequence. A load test is expensive to build and answers the question for one traffic shape on one day. Run it after you know where the cliffs are, to confirm the two or three crossing points you predicted, rather than as a fishing expedition for cliffs you have not mapped.
Three weeks is plenty, if you spend the first day on the graph instead of the last night on the incident. Ask the question early: what actually breaks at 3x? If nobody can answer with a component name and a multiplier, that is the first thing to fix, and it takes about thirty minutes to start.
Key Takeaways
Key points
- ✓Capacity planning fails when it scopes to the service receiving the traffic instead of the dependency chain behind it.
- ✗The classic first casualties: database connection pools, shared caches, NAT gateways, third-party rate limits, and account-level quotas.
- ✗Autoscaling amplifies downstream pressure: more app instances means more connections, more egress, and more API calls hitting fixed limits.
- ✓The right method is mechanical: walk every hop downstream, attach each component's actual configured limit, and multiply.
- ✓Load tests validate the map; they do not replace it. Test after you know where the cliffs are, not to discover them.