Types of Performance Testing
Performance testing is an umbrella term covering smoke, load, spike, soak, stress and volume tests — each answers a different capacity or stability question. Learn which type to pick for the question you are trying to answer.
What is this?
Performance testing measures how a system behaves under load: response times, throughput and resource usage. It is not one activity but a suite — smoke, load, spike, soak, stress and volume tests each answer a different question about capacity and stability. The goal is to validate that a service meets its response-time and error-rate targets at expected traffic, and to discover the load at which it degrades.
Why do you need it?
- Catches regressions that functional tests miss — a slow endpoint or exhausted connection pool only appears under concurrent load.
- Establishes capacity: you learn how many users or requests the system can sustain before latency blows up.
- Validates SLOs and budgets against realistic traffic patterns before they become customer-facing incidents.
- Reveals resource exhaustion: thread pools, database connections, CPU, memory and file descriptors all fail differently under pressure.
- Provides a repeatable, automated gate in CI so performance is tracked like any other quality attribute.
- Spike and soak tests expose failure modes a steady load test cannot, such as GC pauses, memory leaks or slow connection reuse.
Mental model
A fitness test
A performance test
You first measure resting heart rate (baseline), then ramp the treadmill until you get out of breath, and finally push to your max to find the breaking point. Performance testing does the same for software: measure normal, ramp up, and find the ceiling.
How it works
- Define measurable targets first: response-time percentiles (p95/p99), error rate and throughput at an expected concurrent-user count.
- Pick the test type that answers the question — smoke, load, spike, soak or stress — and configure a realistic workload model.
- Generate the load with a tool such as k6, JMeter or Gatling while the system is instrumented with metrics, logs and traces.
- Collect results: latency percentiles, throughput and error rate, plus server-side CPU, memory, GC and database metrics.
- Analyze results against the baseline and targets, identify bottlenecks, fix, and re-run until the targets are met.
Choosing the right type of performance test based on the question you are asking.
Key commands
k6 run --scenario smoke script.jsRun only the smoke scenario to verify the script and the target pipeline.
scenario: smoke ✓✓ 1 iterations · 0 failed
k6 run --scenario load script.jsRun only the sustained load scenario from the multi-scenario script.
k6 run --scenario spike script.jsRun only the spike scenario to exercise sudden, large load jumps.
ab -n 1000 -c 50 http://localhost:8080/api/healthQuick concurrency probe with ApacheBench as a sanity check before full-scale tests.
Time per request: 12.34 [ms] (mean)
hey -z 30s -c 100 http://localhost:8080/api/healthGenerate 30 seconds of sustained load with 100 concurrent workers using hey.
jmeter -n -t load-test.jmx -l results.jtlRun a JMeter plan headlessly and record every sample to a results file.
Examples
Smoke, load and spike profiles in one k6 script
import http from "k6/http"import { check } from "k6"import { sleep } from "k6" export const options = { scenarios: { smoke: { executor: "shared-iterations", vus: 1, iterations: 1, startTime: "0s", }, load: { executor: "ramping-vus", startVUs: 0, stages: [ { duration: "30s", target: 50 }, { duration: "2m", target: 50 }, { duration: "30s", target: 0 }, ], startTime: "1m", }, spike: { executor: "ramping-vus", startVUs: 0, stages: [ { duration: "5s", target: 500 }, { duration: "10s", target: 500 }, { duration: "30s", target: 0 }, ], startTime: "4m30s", }, },} export default function () { const res = http.get("http://localhost:8080/api/health") check(res, { "status is 200": (r) => r.status === 200, "p95 under 200ms": (r) => r.timings.duration < 200, }) sleep(1)}Three scenarios run from the same script: a one-iteration smoke check, a sustained load phase at 50 VUs, and a spike to 500 VUs. Each answers a different question — does the pipeline work, can it sustain expected traffic, and does it survive a sudden surge? The startTime fields sequence them so all three run in a single k6 run.
In production
- A weekly scheduled load test in staging reuses the same scenarios used in production capacity reviews.
- Smoke tests run in every CI merge to catch gross performance regressions within minutes.
- Soak tests run overnight against every release candidate to catch memory leaks that only appear after hours.
- Spike tests are paired with autoscaling drills to confirm the fleet can absorb a traffic surge before a launch event.
Best practices
- Start with a smoke test, then load, then spike or soak — never jump straight to a full-scale stress test.
- Pin every test to a specific question and target; a test without an SLO is just a benchmark.
- Run against realistic data volume and a realistic mix of endpoints, not a single happy-path URL.
- Use a dedicated test environment that matches production in configuration — pool sizes, limits and replica counts.
- Freeze both the application version and the infrastructure during the test window so results are comparable.
Common mistakes
- Treating 'performance test' as a single event instead of a suite of test types with different questions.
- Testing against the staging database's tiny dataset — indexes that are fast in QA never show up as slow in prod.
- Reading results before caches, JIT compilers and connection pools have stabilized during the warm-up window.
- Running the test from a laptop or a single box that becomes the bottleneck itself.
Common issues
Timeout while waiting for a responseServer-side resource exhaustion (thread pool, connections) under load.
Take a thread dump and connection-pool metrics during the test, then size pools up or optimize the bottleneck.
Load generator CPU at 100% and error rate climbingThe client cannot keep up with the requested concurrency.
Move to distributed load generators or lower concurrency so the server is the bottleneck.
Results vary wildly between identical runsShared infrastructure or noisy neighbors interfering with the test.
Use a dedicated environment or repeat the test several times and report the median across runs.
Debugging tips
- Check the load generator's own CPU and network first — a saturated client invalidates every result.
- Correlate the client-side latency curve with server-side CPU and GC: flat latency with climbing CPU means the server is absorbing the load.
- Record thread and connection-pool metrics; exhausted pools cause timeouts that look like network faults.
- Re-run the same test twice — if results are wildly different, the environment is noisy, not the app.
Interview checkpoint
Hands-on lab
- Map testing goals to the correct performance test type
- Write a k6 script with smoke and load scenarios
- Run each scenario and inspect the summary report
- Interpret latency percentiles and error rate against targets
Classify and Run Your First Load Test
30 min · beginner
Quick quiz
Which test type pushes the system beyond its expected capacity to find the breaking point?
A smoke test is designed to validate the full test pipeline with minimal load before running large-scale tests.
You need to find memory leaks that only surface after several hours of steady usage. Which test type is appropriate?
Cheat sheet
SmokeVerify pipeline with minimal loadLoadExpected traffic, fixed windowStressPush past capacity to breaking pointSpikeSudden burst, then observe recoverySoakSteady load for hours, find leaksVolumeLarge data volumes, find data-path limits