1. Home
  2. Blog
  3. Software Testing

Software Testing

Load Testing vs Stress Testing: Differences, Metrics and k6 Examples

One proves your system handles the traffic you expect. The other finds out where it breaks and how it recovers. Learn when to run each, which numbers to trust, and how to plan a test that gives real answers.

9 min read
Load testing vs stress testing: differences, metrics and k6 examples

Key takeaways

  • Load testing checks that a system meets its targets under expected traffic.
  • Stress testing pushes past capacity to find the breaking point and study recovery.
  • The tools are usually the same. What differs is the goal and the traffic pattern.
  • Judge results with percentiles such as p95 and p99, since averages can hide serious problems.

Your application works perfectly with ten users. What happens on sale day, with ten thousand? Load testing vs stress testing describes two different ways to answer that, and mixing them up leads to tests that do not prove what you think they do.

This guide explains what each one shows, the related test types and how to read the results. We ran the small percentile calculation below on Python 3.13, with simulated numbers. The k6 scripts are illustrative examples that we did not execute, and the user counts and targets are invented, so replace them with your own. Only run these tests against systems you own or have permission to test, since heavy traffic can look like an attack and cause real outages.

The short answer

A load test asks: can we handle the traffic we expect, and meet our targets while doing it? A stress test asks: how much can we take before we fail, how do we fail, and how quickly do we recover?

Think of a bridge. A load test drives the expected number of trucks across and confirms that it is fine. A stress test keeps adding weight to learn its limit, and whether it gives warning first. Fortunately, software is cheaper to break than a bridge.

Load testing vs stress testing side by side

Aspect Load testing Stress testing
Main question Do we meet targets at expected load? Where and how do we break?
Traffic level Normal to expected peak Beyond expected peak
Pattern Ramp up, hold steady Keep increasing until failure
Failure A failed test The expected outcome
Key metrics Response time, throughput, errors Breaking point, failure mode, recovery time
Typical timing Before each major release or event After the system is stable under load
Result Pass or fail against targets A capacity limit and a list of weak points

What is load testing?

Load testing simulates the realistic traffic you expect, and measures how the system behaves. It needs a target to test against, for example: “at 500 concurrent users, 95% of checkout requests finish within 800 ms, with under 1% errors.”

Here is how that could look as a k6 script. k6 tests are written in JavaScript, and thresholds turn your targets into a pass or fail result:

import http from "k6/http";
import { sleep } from "k6";

export const options = {
  stages: [
    { duration: "5m", target: 500 },   // ramp up
    { duration: "20m", target: 500 },  // hold at expected peak
    { duration: "2m", target: 0 },     // ramp down
  ],
  thresholds: {
    http_req_duration: ["p(95)<800"],
    http_req_failed: ["rate<0.01"],
  },
};

export default function () {
  http.get("https://staging.example.com/products");
  sleep(1);   // think time, as real users pause
}

The sleep matters. Without think time, each virtual user hammers the server continuously, producing far more traffic than the same number of real people would.

What is stress testing?

Stress testing deliberately goes beyond expected levels. You are not trying to pass. You want answers to questions such as:

  • At what level does performance start to fall apart?
  • Which component fails first: the database, the application servers or a third-party call?
  • Does it fail gracefully with clear errors, or does it crash and lose data?
  • Does it recover by itself when traffic drops?
export const options = {
  stages: [
    { duration: "5m", target: 500 },    // expected peak
    { duration: "5m", target: 1000 },   // beyond it
    { duration: "5m", target: 2000 },   // further
    { duration: "5m", target: 4000 },   // likely to break
    { duration: "10m", target: 0 },     // recovery: does it come back?
  ],
};

That final recovery stage is frequently forgotten, and it may be the most valuable one. A system that handles failure well but never recovers without a manual restart still means an outage for someone to fix in the middle of the night. Ideally, under extreme load, a system should shed work in a controlled way, returning clear 503 responses instead of timing out.

Spike, soak and other types

Load and stress tests belong to a broader family of performance testing:

Type Pattern What it finds
Smoke (performance) A few users, briefly Whether the script and system work at all
Load Expected peak, held Whether targets are met
Stress Rising past capacity Limits and failure modes
Spike A sudden jump, then a drop Autoscaling and queue behaviour
Soak (endurance) Normal load for hours Memory leaks, connections that run out
Capacity Gradual increase The maximum load within your targets

Soak tests catch problems that short tests never see. A small memory leak can look harmless for twenty minutes, and only bring the service down after two days. Spike tests suit events such as flash sales, where traffic does not build gradually.

Metrics that matter

Metric Meaning Watch for
Response time (percentiles) How long requests take p95 and p99 rising
Throughput Requests handled per second Flattening as users increase
Error rate Percentage of failed requests Any sudden rise
CPU and memory Server resource use Saturation, or steady growth
Database Slow queries, connection pool use Pool exhaustion, lock waits
Queue depth Work waiting to be processed Growth that never drains

Why averages mislead

Averages hide the slow requests that real people complain about. To show this, we generated 100 simulated response times, where 95 were fast (80 to 200 ms) and 5 were very slow (2.5 to 4 seconds), then calculated the common statistics:

import math, random, statistics

random.seed(3)
times = [random.randint(80, 200) for _ in range(95)] \
      + [random.randint(2500, 4000) for _ in range(5)]

def pct(values, p):
    values = sorted(values)
    return values[math.ceil(p / 100 * len(values)) - 1]

print("avg", round(statistics.mean(times)), "p50", pct(times, 50),
      "p95", pct(times, 95), "p99", pct(times, 99), "max", max(times))

# avg 302 p50 150 p95 199 p99 3740 max 3896

Look closely at those numbers. The average of 302 ms describes nobody. Typical requests took about 150 ms, while the slow ones took over 3 seconds. Even p95 looks healthy at 199 ms, and only p99 reveals the problem. A figure like p95 means 95% of requests were this fast or faster.

And one percent of requests is not a rare event. If a page makes 50 requests, a slow p99 affects a large share of page views. Set targets on the percentiles that matter to you, and look at several together.

Tip: Look for the “knee” in your graphs. As load increases, response times stay flat for a while, then suddenly climb, while throughput stops growing. That point is your practical capacity, and it often comes well before anything actually crashes.

Tools

Tool Scripting Known for
k6 JavaScript Developer-friendly, thresholds suit CI
JMeter GUI and XML Mature, supports many protocols
Gatling Scala, Java, Kotlin and others Code-based, efficient
Locust Python Simple, Python-based user behaviour

Any of these can run both load and stress tests. Pick the one that fits your team’s skills, rather than chasing a feature list. To use one in your pipeline, see what is CI/CD.

How to plan a test

  1. Set goals. Use real analytics to find your peak traffic, and define targets.
  2. Choose journeys. Model realistic user behaviour, such as browse, search and checkout, in realistic proportions. Hitting only the home page proves little.
  3. Use a realistic environment. A tiny staging server cannot predict production. Note any differences.
  4. Prepare data. Use realistic volumes, because a query that is fast on 100 rows may crawl on 10 million.
  5. Warn people. Tell your operations team and any third parties, and do not load test a payment provider by accident. Mock external services where appropriate.
  6. Start small. Run a small smoke test first to confirm that your script works.
  7. Monitor everything. Without server metrics, you will see that it slowed down, but not why.
  8. Repeat. Run it again after each fix, and keep a baseline to compare against.

Reading the results

Symptom Possible cause
Slow responses, but low CPU Waiting on a database, lock or external service
Database CPU is high Missing indexes or inefficient queries
Sudden connection errors Connection pool or file limit reached
Memory climbs steadily A leak, or an unbounded cache
Load generator CPU is high The test tool is the bottleneck, not your system

Databases are a common bottleneck, so if queries are the issue, read our guide to SQL query optimization. That final row of the table is an important trap: always check the health of the machine generating the load as well.

Common mistakes

  • Confusing users with requests. Be clear whether a target means concurrent users or requests per second.
  • Leaving out think time. This inflates traffic and gives misleading results.
  • Testing too late. A week before launch leaves no time to fix architecture problems.
  • Using a single endpoint. Real traffic mixes reads, writes and searches.
  • Caching hides everything. Identical repeated requests may only test your cache, so vary the data.
  • Testing once. Performance changes with every release, so run tests regularly.

Final thoughts

Run load tests to confirm that you can handle the traffic you expect, and stress tests to learn what happens when you cannot. Start small: choose your most important user journey, set one clear target, and run a short test against staging with monitoring on. That first run usually teaches you something, and you can build from there.

Frequently asked questions

What is the main difference between load and stress testing?

Load testing checks behaviour under expected traffic against set targets. Stress testing goes beyond capacity to find the breaking point and how the system recovers.

Should I run load testing or stress testing first?

Load test first. Once the system meets its targets under expected traffic, run stress tests to explore its limits. Stress results mean little on an unstable system.

What is the difference between a stress test and a spike test?

A stress test typically raises load step by step until failure. A spike test jumps suddenly to a high level, to see how the system copes with abrupt change.

What is p95 response time?

It means 95% of requests finished at or below that time. It describes the experience of most users better than an average does.

Can I run load tests on production?

You can, but carefully, with permission, monitoring and a way to stop quickly. Most teams begin with a production-like staging environment to reduce the risk to real users.

Keep learning

Work with Stack Quora

Need help with testing, WordPress or SEO?

We help teams ship better software and grow their sites. Tell us what you are working on and we will suggest a clear next step.

Keep reading

More guides on testing, AI & modern development

Enjoyed this article? Pick a topic and jump straight into our newest tutorials.

Artificial Intelligence

LLM Structured Output Explained: Getting Reliable JSON From AI Models

LLM structured output explained with validation we ran in Pydantic: JSON mode and schemas, constrained decoding, the ways model output…

Read article →
DevOps

SSH Keys Explained: How They Work, ssh-keygen, Ed25519 and Safe Habits

SSH keys explained with keys we generated: public vs private keys, ssh-keygen, Ed25519 vs RSA, fingerprints, passphrases, ssh-agent, authorized_keys, config…

Read article →
DevOps

HTTP Caching Explained: Cache-Control, ETag and 304 Responses, With a Live Test

HTTP caching explained with a small server we ran: Cache-Control directives, max-age, no-cache vs no-store, ETag revalidation and 304 responses,…

Read article →
Test Automation

API Contract Testing Explained: Catch Breaking Changes Before They Ship

API contract testing explained with a schema check we ran: what a contract is, consumer-driven contracts, Pact, OpenAPI, which changes…

Read article →
Software Testing

Risk-Based Testing Explained: Score Risks, Build a Matrix and Test What Matters

Risk-based testing explained step by step: identifying risks, scoring likelihood and impact, building a risk matrix, mapping risk to test…

Read article →
Databases & SQL

SQL Constraints Explained: NOT NULL, UNIQUE, CHECK and FOREIGN KEY, Tested

SQL constraints explained with tests we ran: NOT NULL, UNIQUE, CHECK, DEFAULT, PRIMARY and FOREIGN KEY, the NULL loopholes, SQLite's…

Read article →
Databases & SQL

SQL Date Functions Explained: Adding, Comparing and Grouping Dates Without Bugs

SQL date functions explained with queries we ran: adding intervals, month-end surprises, date differences, the BETWEEN trap, grouping by day,…

Read article →
Software Development

Python asyncio Explained: async, await, gather and TaskGroup With Real Timings

Python asyncio explained with code we timed: coroutines, await, gather, TaskGroup, error handling, timeouts, limiting concurrency and the blocking-call mistake…

Read article →
Software Development

Debounce vs Throttle in JavaScript: How They Work, When to Use Each, Tested

Debounce vs throttle in JavaScript explained with timings we measured in Node.js: how each works, simple implementations, leading and trailing…

Read article →
Software Development

Python Context Managers Explained: The with Statement, contextlib and Real Uses

Python context managers explained with code we ran: how with works, __enter__ and __exit__, contextlib.contextmanager, suppress, ExitStack, database transactions and…

Read article →
Artificial Intelligence

LLM Temperature and Top-p Explained: How AI Models Choose Their Next Word

LLM temperature and top-p explained with a simulation we ran: how probabilities are reshaped, what top-p keeps, recommended settings by…

Read article →
DevOps

YAML Explained: Syntax, the Norway Problem and Other Traps We Tested

YAML explained with a parser test: basic syntax, lists and maps, the Norway problem, versions turning into floats, octal zip…

Read article →
DevOps

Cron Jobs Explained: Syntax, Real Schedules and the Mistakes That Break Them

Cron jobs explained with schedules we verified: the five fields, operators, real examples, the day-of-month OR day-of-week trap, environment problems,…

Read article →
Software Testing

Bug Life Cycle in Software Testing: Every Status, Who Owns It and Common Mistakes

The bug life cycle explained: every defect status from New to Closed, who owns each step, rejected, deferred and reopened…

Read article →
Test Automation

Property-Based Testing in Python: Hypothesis Finds the Bugs Your Examples Miss

Property-based testing explained with Hypothesis tests we ran: properties vs examples, strategies, shrinking, the real bugs it found in our…

Read article →
Databases & SQL

SQL Pagination Explained: OFFSET vs Keyset, With a Million-Row Test

SQL pagination explained with a million-row test we ran: LIMIT and OFFSET, why deep pages get slow, keyset (cursor) pagination,…

Read article →
Databases & SQL

SQL EXISTS vs IN: Differences, the NOT IN NULL Trap and Performance

SQL EXISTS vs IN explained with queries we ran: how each works, why NOT IN returns no rows when a…

Read article →
Software Development

Python Logging Explained: Levels, Handlers, Formatters and Mistakes to Avoid

Python logging explained with code we ran: levels, basicConfig quirks, named loggers, handlers and formatters, logger.exception, lazy formatting, duplicate logs…

Read article →
Software Development

JavaScript Map vs Object: Key Differences, Pitfalls and a Benchmark

JavaScript Map vs Object explained with code we ran in Node.js: key types, ordering, the prototype trap, JSON, size, WeakMap…

Read article →
Software Development

Python Type Hints Explained: Annotations, mypy and the Errors It Catches

Python type hints explained with a real mypy run: basic annotations, Optional and unions, TypedDict, Literal, Protocol, generics, Callable, strict…

Read article →
Artificial Intelligence

LLM Tokens Explained: How AI Models Read Text, Count Costs and Fill Context Windows

LLM tokens explained with a tokenizer we ran: how text is split, why counts differ by language and tokenizer, context…

Read article →
DevOps

Semantic Versioning Explained: MAJOR.MINOR.PATCH, Ranges and Release Rules

Semantic versioning explained with code we ran: MAJOR.MINOR.PATCH rules, pre-releases, correct version sorting, caret and tilde ranges, 0.x versions, lockfiles…

Read article →
DevOps

Git Reset vs Revert: How to Undo Commits Safely, With a Tested Example

Git reset vs revert explained with a repository we built: soft, mixed and hard reset, revert commits, reverting merges, recovering…

Read article →
Software Testing

The Test Pyramid Explained: Layers, Ratios and When to Break the Shape

The test pyramid explained: unit, integration and end-to-end layers, sensible ratios, the ice cream cone anti-pattern, the testing trophy and…

Read article →
Test Automation

pytest Fixtures Explained: Scope, yield Teardown, conftest.py and Factories

pytest fixtures explained with a suite we ran: how fixtures work, scopes, yield teardown, conftest.py, factory fixtures, parametrized fixtures, built-ins…

Read article →
Databases & SQL

SQL Upsert Explained: INSERT ON CONFLICT, MERGE and the REPLACE Trap

SQL upsert explained with queries we ran: INSERT ON CONFLICT DO UPDATE and DO NOTHING, the excluded row, conditional updates,…

Read article →
Databases & SQL

SQL LIKE Operator Explained: Wildcards, Case Sensitivity and Performance

The SQL LIKE operator explained with queries we ran: % and _ wildcards, escaping, NOT LIKE and NULL, case sensitivity…

Read article →
Artificial Intelligence

AI Hallucinations Explained: Why Language Models Make Things Up and How to Reduce It

AI hallucinations explained: what they are, why language models produce them, common types, how to reduce them with grounding and…

Read article →
Test Automation

Mocking in Python: unittest.mock, patch and the Mistakes That Make Tests Lie

Mocking in Python with unittest.mock, tested: Mock and return_value, side_effect, patching where a name is used, spec and autospec, mocking…

Read article →
Software Testing

Code Coverage Explained: Line vs Branch Coverage and What 100% Really Means

Code coverage explained with a real coverage.py run: line, branch and condition coverage, reading a report, why 100% coverage can…

Read article →
Artificial Intelligence

What Are AI Agents? How They Work, With a Simple Agent Loop You Can Run

What are AI agents? A plain guide to the agent loop, tools, memory and guardrails, with a small Python loop…

Read article →
Test Automation

TDD vs BDD: Key Differences, One Feature Built Both Ways and When to Use Each

TDD vs BDD explained with one discount feature built both ways: red-green-refactor with code we ran, Given-When-Then scenarios, tools, trade-offs…

Read article →
Software Testing

User Acceptance Testing (UAT): Process, Checklist and Real Examples

User acceptance testing explained: who runs it, how it differs from QA, a seven-step process, a UAT test case template,…

Read article →
Artificial Intelligence

What Are Embeddings? Vectors and Cosine Similarity Explained Simply

What are embeddings? A plain-language guide to vectors, cosine similarity and semantic search, with a small example we ran, common…

Read article →
Test Automation

Flaky Tests: What Causes Them and How to Fix Them for Good

Flaky tests explained with demos we ran: the main causes, how to confirm and diagnose one, proper fixes for timing,…

Read article →
Software Testing

Load Testing vs Stress Testing: Differences, Metrics and k6 Examples

Load testing vs stress testing explained: what each proves, spike and soak tests, the metrics that matter, why averages mislead,…

Read article →