Load Testing: How to Find Your System's Breaking Point Before Users Do

A practical guide to load testing — load, stress, spike and soak tests, realistic workload modeling, key metrics, finding bottlenecks, tools like k6 and Locust, and testing safely.

By Danyal Ahmed - Data Scientist · · 4 min read

The worst time to discover your system can't handle traffic is during a product launch, a marketing campaign or Black Friday. Load testing simulates real traffic in a controlled way so you learn your capacity limits, find bottlenecks and verify that scaling works, before your users do it for you.

Key takeaways

  • Different tests answer different questions: load (expected traffic), stress (breaking point), spike (sudden surges), soak (long-duration stability).
  • Realistic workload modeling matters more than raw request volume: user journeys, data variety, think time and traffic mix.
  • Watch latency percentiles, error rates and throughput, alongside resource saturation on every tier.
  • The goal isn't a number. It's finding the first bottleneck, fixing it, and repeating.
  • Test in a production-like environment, and never load-test systems you don't own or third-party APIs without permission.

Types of performance tests

Test Question Shape
Load test Do we meet SLOs at expected peak traffic? Ramp to target, hold
Stress test Where and how do we break? Ramp beyond expected until failure
Spike test Can we survive sudden surges? Jump from baseline to high instantly
Soak (endurance) test Do we degrade over hours? (leaks, connection exhaustion) Moderate load for hours
Capacity test How much traffic per instance / per dollar? Stepwise increases, measuring efficiency

Step 1: Define goals and SLOs

Be specific:

  • "Checkout API p95 < 300 ms and error rate < 0.1% at 800 requests/second."
  • "Homepage handles 5× normal traffic within 2 minutes via autoscaling."
  • "No memory growth over 8 hours at typical load."

Derive targets from real traffic data and growth projections (capacity planning).

Step 2: Model a realistic workload

The most common mistake is hammering a single endpoint (often a cached one) and declaring victory. Real traffic is a mix:

text60% browse product pages
20% search
10% add to cart
 7% view cart
 3% checkout

Also model:

  • Think time between user actions.
  • Data variety: different products, users and search terms, so you don't hit the same cache key on every request.
  • Authenticated vs anonymous users.
  • Arrival rate: model requests per second arriving independently (an open model) rather than a fixed pool of users waiting on responses (a closed model), when you want to see how the system behaves as it slows down.

Step 3: Write the test

Example with k6 (JavaScript-based, great for developers):

jsimport http from "k6/http";
import { check, sleep } from "k6";

export const options = {
  scenarios: {
    browse: {
      executor: "ramping-arrival-rate",
      startRate: 50, timeUnit: "1s",
      preAllocatedVUs: 200, maxVUs: 2000,
      stages: [
        { target: 400, duration: "5m" },   // ramp up
        { target: 400, duration: "15m" },  // hold at expected peak
        { target: 0, duration: "2m" },     // ramp down
      ],
    },
  },
  thresholds: {
    http_req_failed: ["rate<0.001"],
    http_req_duration: ["p(95)<300", "p(99)<800"],
  },
};

const products = JSON.parse(open("./product_ids.json"));

export default function () {
  const id = products[Math.floor(Math.random() * products.length)];
  const res = http.get(`${__ENV.BASE_URL}/api/products/${id}`);
  check(res, { "status 200": (r) => r.status === 200 });
  sleep(Math.random() * 2);
}

Other popular tools: Locust (Python), Gatling (JVM), JMeter, and Artillery. Choose based on your team's language and scripting needs.

Step 4: Use the right environment

  • Production-like: same instance types, configurations, database size and data distribution. Tests against a tiny staging database with 1,000 rows are misleading.
  • Isolated: don't hurt real users. If you test production (sometimes the only realistic option), do it off-peak, ramp carefully, and coordinate with on-call staff.
  • Mock or sandbox third parties: don't send load to payment providers, email services or external APIs without their permission.
  • Generate load from enough machines, so the load generator isn't the bottleneck.

Step 5: Measure the right things

From the load tool:

  • Throughput (requests/second).
  • Latency percentiles: p50, p95, p99. Averages hide tail latency.
  • Error rate and error types (timeouts vs 5xx vs 4xx).

From the system (observability explained):

  • CPU, memory, network and disk per tier.
  • Database: connections, slow queries, locks, replication lag, cache hit ratio.
  • Connection pools and thread pools: saturation and wait times (connection pooling explained).
  • Queues: depth and consumer lag.
  • Autoscaling events and how long new instances take to become healthy.
  • Traces of slow requests to see where time goes.

Step 6: Find the bottleneck, fix, repeat

Watch how the system degrades as load rises:

textThroughput rises linearly … then flattens (saturation) … latency climbs sharply … errors appear

The component that saturates first is your bottleneck. Common culprits:

Symptom Likely bottleneck
DB CPU high, slow queries Missing indexes, N+1 queries (how database indexes work)
Requests waiting, low CPU Connection or thread pool exhaustion, locks
Latency climbs, app CPU at 100% Inefficient code, need horizontal scaling
Errors from a dependency Downstream rate limits or capacity
Memory growth in soak test Leaks, unbounded caches
Spike causes errors, then recovers Autoscaling too slow; needs headroom or queuing

Fix the bottleneck, then re-run. The next bottleneck will appear. Continue until you meet targets with a safety margin. The overall scaling playbook: how to scale a web application.

Step 7: Automate

  • Run a baseline performance test in CI for critical endpoints on important changes (CI/CD pipeline guide).
  • Compare results against previous runs, and flag regressions in p95 latency or throughput.
  • Run full-scale tests before launches and seasonal peaks.

Test failure scenarios too

Combine load with failure:

  • Kill an instance mid-test: does traffic redistribute cleanly?
  • Flush the cache: can the database survive a cold cache (caching strategies)?
  • Slow down a dependency: do timeouts and circuit breakers protect the rest of the system?

This is the gateway to chaos engineering: verifying resilience deliberately rather than hoping for it.

FAQ

How often should I load test?

Before major launches, before expected peaks, and after significant architectural changes. Lightweight automated performance tests on critical paths can run much more frequently, even per release.

Can I load test production?

Sometimes it's the most realistic option, but it carries risk. Use careful ramping, off-peak windows, clear abort criteria, monitoring, synthetic test accounts, and coordination with your team. Never direct load at third-party services without permission.

What's the difference between load testing and stress testing?

Load testing verifies behavior at expected traffic levels against your targets. Stress testing pushes beyond expected levels to find the breaking point and see how the system fails and recovers.

How many virtual users do I need?

Think in terms of arrival rate (requests per second) derived from real traffic, rather than an arbitrary user count. Convert expected peak users and actions per minute into a request rate and a traffic mix.