Profile    Mohammed Shiroz Status   Loading  
Logo
Share This
Back to blog
Filter by:
Tags
//Article title

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

About Post

Every application has a breaking point. A number of users, or requests per second, where pages slow to a crawl, errors start appearing and the database quietly gives up.

You don't get to choose whether your app has one. You only get to choose when you find out. On a quiet afternoon, with a test you control and a coffee in your hand? Or on the morning of a big launch, the first day of the month when everyone pays rent, or the day a post about your product goes viral?

Load testing is how you pick the quiet afternoon. It sounds like specialist work, but the basics are very approachable. Here's the process I'd follow, step by step.

Step 1: ask a specific question

"Is the app fast?" isn't a question a load test can answer. These are:

  • Can the checkout flow handle our busiest expected hour with p95 response times under 500 ms?
  • At what number of concurrent users does the error rate go above 1%?
  • Does the API stay stable for four hours of normal traffic, or does memory slowly climb?
  • What happens when traffic jumps suddenly, like after a push notification goes out to every user?

Each of those is a different kind of test, and knowing which one you're running changes everything else.

Test typeWhat it doesAnswers
Load testExpected traffic, held steadyDo we meet our targets on a normal busy day?
Stress testKeep increasing until things failWhere is the breaking point, and how do we fail?
Spike testA sudden jump in trafficDo we survive a burst, and recover after it?
Soak testNormal traffic for hoursLeaks, growing queues, connections not being released

Step 2: pick where to test (not production)

Test against a staging environment that's as close to production as you can afford: same instance sizes, same database engine and configuration, a realistic amount of data. A query that's instant on a table with 500 rows can be painfully slow on the real one, so an empty staging database gives you a happy, useless result.

Two warnings. Don't load test production unless everyone involved has agreed to it and you know how to stop. And make sure your test doesn't hammer third parties: point payment gateways, SMS and email at sandboxes or fakes. Load testing someone else's API without permission is not a nice surprise for them.

Step 3: model what real users do

The most common load testing mistake is hitting one endpoint as fast as possible. Real users don't do that. They log in, browse a list, open a detail page, pause to read, maybe submit a form. Some actions are common, some are rare but expensive (a report export, a search with many filters).

A realistic scenario includes:

  • A mix of journeys, weighted roughly like your real traffic. Your access logs or analytics are the best source.
  • Think time between actions, so 100 virtual users behave like 100 people, not 100 scripts.
  • Varied data: different users, different IDs, different search terms. If every request asks for the same record, you're mostly testing your cache.

Step 4: write the test

There are good tools for this: k6 (scripts in JavaScript, runs from the command line), Apache JMeter (long-established, with a GUI), Gatling and Locust (scenarios in Python). I like k6 because the test is just code you can keep in the repository. A simple version:

import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  stages: [
    { duration: '2m', target: 50 },   // ramp up
    { duration: '5m', target: 50 },   // hold at expected load
    { duration: '3m', target: 200 },  // push past it
    { duration: '2m', target: 0 },    // ramp down
  ],
  thresholds: {
    http_req_duration: ['p(95)<500'],
    http_req_failed: ['rate<0.01'],
  },
};

export default function () {
  const res = http.get('https://staging.example.com/api/invoices?page=1');
  check(res, { 'status is 200': (r) => r.status === 200 });
  sleep(Math.random() * 3 + 1); // think time: 1 to 4 seconds
}

Run it with k6 run load-test.js. The thresholds turn your question into a pass or fail, which means you can run the same test in CI later and get a clear answer. A real script would also log in and walk through a few journeys; this one is simplified to show the shape.

Step 5: read the right numbers

A load test produces a lot of output. These are the numbers that matter:

  • Percentiles, not averages. An average of 200 ms can hide the fact that one request in twenty takes four seconds. Look at p95 and p99: what the slowest 5% and 1% of users actually experience.
  • Error rate. Timeouts, 500s and 502s. Fast errors are still errors, and some systems get "faster" when they start failing.
  • Throughput. Requests per second actually served. When you add users but throughput stops rising, you've found a ceiling.
  • Server-side metrics at the same time: CPU, memory, database connections, slow query log, queue length. The load tool tells you that it got slow. These tell you why.

The breaking point is a curve, not a crash. Plot response time against users. It stays flat, then bends, then shoots up. The bend is where your real capacity is, and it usually arrives well before anything actually falls over.

Step 6: find the bottleneck

In web apps, the same suspects show up again and again:

  • The database. Missing indexes, N+1 queries that were invisible with one user, and locks when many requests update the same rows. Usually the first thing to break.
  • Connection limits. Database max connections, or the number of PHP-FPM workers (pm.max_children). When all workers are busy, new requests queue, and response times climb even though CPU looks fine.
  • Slow external calls made during the request. Each one holds a worker hostage while it waits.
  • Missing caching on expensive, rarely changing data like settings, lookups or dashboard counts.
  • The load generator itself. If the machine running k6 maxes out its own CPU or network, you're measuring your laptop, not your app.

Step 7: fix one thing, test again

Change one thing at a time and re-run the same test, so you know which fix helped. Keep the results of each run; a short table of "before and after" is the most convincing performance argument you'll ever bring to a planning meeting.

And keep the script. The best time to run a load test is before every big release, not once a year after something went wrong.

The checklist

  • Start with a specific question and pick the matching test type.
  • Test a production-like environment with realistic data, never third-party live APIs.
  • Model real journeys with think time and varied data.
  • Use thresholds so the test passes or fails clearly.
  • Watch p95/p99, error rate and throughput, alongside server metrics.
  • Fix one bottleneck at a time and re-run.

What broke first the last time you load tested something: the database, the workers, or something you never expected?

Comments (0)
Leave your review

Thanks for your valuable comments. Your comments has been updated and appreciate your getting in touch...

01. About Shiroz

Mohammed Shiroz

Hi, I'm Mohammed Shiroz, a software engineer and AI enthusiast from Sri Lanka who turns ideas into intelligent, real-world solutions. With over 9 years of hands-on experience, I currently lead real estate ERP development at Kate Group, a...

03.My Projects

04. Categories

Ready To order Your Project ?

Get in Touch
Close