Why setTimeout is Lying to Your Retry Logic
You've written retry logic. It probably looks something like this: async function withRetry ( fn , retries = 3 ) { for ( let i = 0 ; i < retries ; i ++ ) { try { return await fn (); } catch ( err ) { if ( i === retries - 1 ) throw err ; await new Promise ( r => setTimeout ( r , 200 * ( i + 1 ))); } } } You test it locally. You simulate a slow dependency like this: const fakeDB = async () => { await new Promise ( r => setTimeout ( r , 200 )); // simulate DB return { id : 1 , name : ' test ' }; }; Your retry logic works. Tests pass. You ship it. Then in production, your app starts dropping requests under load. The problem isn't your retry logic. It's your fake. Real dependencies don't have flat latency Here's what your Postgres instance actually looks like in production: p50: 5ms — half of all queries finish in under 5ms p95: 50ms — 95% finish under 50ms p99: 200ms — 99% finish under 200ms p99.9: 2000ms — that one unlucky query during a GC pause Your setTimeout(fn, 200) simulates the worst case, every single time. That's not how production works. And because it's not how production works, your retry logic has never actually been tested against reality. The bugs hide in the variance — not in the slow case, but in the unpredictability. What the real distribution looks like Latency in distributed systems follows a lognormal distribution . It's right-skewed: most requests are fast, a meaningful minority are slow, and a small tail is very slow. This shape comes from how real systems work: GC pauses — Java, Go, and even Node's garbage collector occasionally stops the world Cold caches — first query after a cache miss is always slower Network jitter — packet routing isn't deterministic Noisy neighbors — other workloads on the same hardware compete for resources Connection pool exhaustion — when all connections are busy, new queries wait None of these are constant. They're random, rare, and multiplicative — which is exactly what produces a lognormal shape. Why this matters fo