Load Testing Before You Get the Traffic You Wanted

SprintX Team

Written By

SprintX Team

AI & Product Engineering

August 14, 2026

8 min read

A load test result graph showing response times degrading as concurrent users increase

A launch that works is the most expensive way to discover your connection pool holds twenty. Here is how to find your breaking point on purpose, first.

The launch you want is the one that breaks your app. A post lands well, a newsletter goes out, a partner mentions you, and four hundred people arrive in ten minutes — which is roughly four hundred times what you have ever tested.

What happens next is rarely a graceful slowdown. Apps that have never been loaded tend to fail sharply: fine, fine, fine, then every request timing out because something finite ran out. Usually database connections. Sometimes memory. Occasionally a third-party API that started rate-limiting you at exactly the wrong moment.

Load testing is how you find that cliff on a Tuesday afternoon instead of during the best hour your product has ever had. It is a half-day exercise for a small app, and the point is not a pass or fail grade — it is learning which thing breaks first, and at what number.

Model a journey, not an endpoint

The most common mistake is hammering one URL with a load tool and concluding the app handles 5,000 requests per second. It does not. What it handles is 5,000 requests per second to a cached, static, unauthenticated route.

Real traffic looks different in three specific ways, and each one is where the failure hides:

  • Users are authenticated. Sessions get validated, tokens get verified, and per-user authorization runs on every query. If your access rules re-evaluate per row, load is exactly when that becomes visible.
  • Users are different people. One test user means one cache entry, one row set, and one very unrealistic hit rate. Generate distinct accounts with distinct data.
  • Users do sequences. Land, sign up, create something, view a list, upload a file. Write your test as that script, with realistic pauses between steps, because the pauses are what produce the concurrency pattern of real traffic rather than an artificial hammer.

Tools like k6, Artillery, and Locust all express this as code. Write the journey once, keep it in the repo, and re-run it before releases — it becomes a regression test for performance, not a one-off.

Pick a number that means something

"Can it handle a lot of traffic" is not testable. Convert your launch expectation into concurrency.

Take the number of people you think will arrive in the busiest hour. Divide by the length of a session in seconds, and multiply by the session duration to get rough concurrent users — or shortcut it: a decent front-page moment on a large aggregator is a few hundred concurrent visitors, a Product Hunt launch day is tens of concurrent visitors spread across many hours, and an email to 10,000 subscribers typically produces its peak in the first fifteen minutes at a few percent open-and-click.

Then test at that number, at twice it, and at five times it. The multiples matter more than the base, because what you are really learning is the shape of the curve: where latency starts rising, and where it goes vertical.

Five test shapes, five different findings

Each profile finds a distinct class of problem, and they are not substitutes for each other.

TestShapeWhat it finds
SmokeA handful of users, a few minutesThat the script and environment work at all
LoadExpected peak, held 15–30 minutesWhether normal launch traffic is survivable
StressRamp until it breaksYour actual ceiling, and what breaks first
SpikeZero to peak in secondsCold starts, autoscaling lag, connection storms
SoakModerate load, 2–4 hoursMemory leaks, connection leaks, disk filling up

The stress test is the one people skip and the one worth the most. Knowing "we degrade at 300 concurrent and fall over at 450" is a number you can plan around. Knowing "the load test passed" tells you nothing about how much headroom you have.

The soak test is the sneaky one. Plenty of apps handle a 20-minute burst perfectly and die after three hours, because every request leaks a database connection or an event listener and nothing reclaims them. If your app has ever needed a restart to feel fast again, run this one.

Make the environment honest

A load test against an empty database on a laptop tells you about your laptop.

Data volume must be realistic. Query plans change with table size. A test against a thousand rows will never show you the sequential scan that appears at a million. Seed production-scale volumes, or as close as you can get, and check the plans for your hot queries at that size — the profiling approach in scaling a Supabase app applies directly.

Caches must start cold. Warm caches flatter you. Run the first minutes of the test as the real thing: nothing cached, everything computed.

The environment must match production shape. Same instance sizes, same connection limits, same regions, same serverless configuration. A staging environment that differs structurally from production produces results that are worse than none, because they are confidently wrong.

Stub or coordinate with third parties. Do not send ten thousand real emails or hammer a partner's sandbox. Stub external calls with realistic latency, or use provider test modes with their limits in mind. Notably, if a dependency rate-limits you in the test, that is a finding, not an obstacle — it will happen in production too.

What you will actually find

Across a lot of these engagements, the same handful of results come up, roughly in this order of frequency.

Connection exhaustion is first and by a distance. Serverless functions each opening a database connection will exhaust a small instance's limit long before CPU matters, and the symptom is a wall of connection errors at a very specific concurrency. The fix is a pooler in transaction mode, and it is often the difference between 40 concurrent users and 4,000.

Then N+1 queries. A list page issuing one query per row is invisible at ten rows and fatal at load. The load test surfaces it as latency that grows with concurrency rather than staying flat.

Then a missing index on the exact query that runs on the busiest page.

Then unbounded work in the request path — a PDF generated, an email sent synchronously, a model call awaited. These do not just slow the request; they occupy a worker that cannot serve anyone else, so throughput collapses far below what CPU suggests. Moving them out is usually the largest single win after the pool fix.

Then, on serverless, cold starts during spikes: the platform scales, but each new instance takes a second or more before it serves anything, and a sharp spike is mostly cold starts.

If your app has already shown symptoms with a handful of simultaneous users, the causes tend to be the same list — why an app crashes with multiple users covers the diagnosis from the other direction.

Fix, re-run, and write down the number

A load test is only useful in pairs. Run it, fix the first thing that broke, run it again. The second run almost always reveals a different bottleneck that was hidden behind the first — you cannot see the N+1 while the connection pool is exhausted.

Two or three rounds is usually enough to move an app from "falls over at 50" to "degrades gracefully at 1,000", and most of that comes from configuration and a few queries rather than architecture.

When you stop, record the ceiling in writing: the concurrency at which latency degrades, the concurrency at which errors start, and what broke. That number tells you when to add capacity, what to alert on before you hit it, and whether a caching layer is worth building yet — the boundaries in choosing a caching strategy are much easier to reason about with a measured ceiling in hand. Wire the thresholds you found into your alerts so you get warned on approach rather than on arrival.

Frequently asked questions

Can I load test production? Carefully, and sometimes it is the only honest option. Do it at a low-traffic hour, ramp gradually with a hand on the stop button, use clearly marked test accounts, and make sure your test writes are easy to identify and delete. Never point a stress test at production without telling everyone who might see the alerts.

How much load testing does a pre-launch app really need? For most early products, half a day: one journey script, a load run at expected peak, a stress run to find the ceiling. That catches the failures that turn a good launch day into an outage. Soak and spike tests are worth adding once you have paying customers who notice.

Do I need to test if I am on serverless with autoscaling? Yes, and arguably more. Autoscaling protects the compute tier, which is rarely what breaks. The database connection limit, third-party rate limits, and cold-start latency all sit outside the thing that scales, and scaling compute aggressively can make the connection problem arrive faster.


If you are about to send traffic at an app that has never seen more than a handful of simultaneous users, the ceiling exists whether or not you know where it is. SprintX runs realistic load tests, fixes what the results expose, and hands back a documented capacity number you can plan against. Tell us when you are launching and we will work backwards from it.

Related Articles

Contact us

to find out how this model can streamline your business!