Performance Testing Techniques
Load, stress, soak, spike, scalability, and volume. Each tests a different failure mode. Knowing which to run, and what a failing result actually means, separates senior testers from juniors.
1 The Hook
An Auckland government agency launches a new digital service. Load testing was run against the test environment — 2 vCPUs, 4 GB RAM. The test passed: 200 concurrent users handled fine.
The production environment is 8 vCPUs, 16 GB RAM. On go-live day, 850 users hit the service simultaneously at 9am. The service falls over at 300 users.
The test environment was under-spec'd compared to production, and the test assumed linear scaling. It didn't scale linearly. Database connection pools, caching warm-up, and thread management all behave differently at higher loads. The test told you nothing useful about production behaviour — it was guessing dressed up as evidence.
Performance testing is a family of six distinct techniques — load, stress, soak, spike, volume, and scalability — each targeting a different failure mode; running only load tests and calling it done is the most common gap. Use it whenever a system handles concurrent users, has an SLA, or faces a predictable demand event (tax year-end, budget announcements, school enrolments). The single biggest mistake testers make is running tests against an under-spec'd environment and extrapolating the results to production — a smaller environment with different caching, connection pool settings, and no CDN will produce numbers that actively mislead rather than inform.
From the field
A NZ council launched a rates portal ahead of annual billing. Load testing used 50 simulated users — well within expected peak. On billing day, 4,200 ratepayers logged in within the first hour and response times hit 45 seconds. The load test had wrong think time and omitted the PDF generation endpoint entirely. Performance testing with unrealistic scenarios is worse than no testing — it creates false confidence.
Every performance test I have ever reviewed was run against a warm system. The caches were primed, the JVM was past its cold-start window, the connection pool was already populated. Then someone deploys at midnight and the system comes up cold at 6am — before the 9am peak arrives. The query plans that looked fine under load now hit full table scans on a cache-empty database. The system that passed load testing at 400ms p95 is now sitting at 4 seconds. I have seen this at two major NZ government agencies and one large KiwiSaver provider. The fix is simple and almost nobody does it: include a mandatory warm-up verification in your go-live runbook, and add a cold-start soak to your test suite. Run the load test immediately after a fresh deployment with empty caches. That is the scenario your production users will actually see on launch morning.
2 The Rule
Run performance tests against a production-representative environment at production-representative load. Testing on a smaller environment and extrapolating is not performance testing — it's guessing.
Common Mistake vs What Works
Run a load test with 100 virtual users for one minute against the staging environment — a 2-vCPU box with 100 rows in the database. P95 comes back at 320ms. Performance sign-off is given. Production runs on 16 vCPUs with 2 million records and 10,000 concurrent users on launch day, and the service collapses before 9:30am. The test didn't replicate production — it replicated a quiet Tuesday morning in an empty warehouse.
Match test conditions to production: realistic concurrent user count, production-equivalent data volumes (seed the database with representative record counts), and the same infrastructure tier — or explicitly document and escalate the gap if that's not possible. Run a soak test (8+ hours at sustained load) to catch memory leaks that short spikes never surface. If you cannot get a production-equivalent environment, label results as indicative, not sign-off, and put the risk in writing before go-live.
3 The Analogy
Performance testing is like a stress test on a bridge before opening it to traffic.
Load testing checks the bridge holds under normal traffic. Stress testing finds the point where it buckles. Soak testing checks whether it holds under sustained load all day. You need all three before opening. Sending 10 cars over a bridge that needs to carry 500 tells you the bridge exists — not that it's safe.
4 Watch Me Do It
A k6 script for an NZ government portal login under combined load and stress conditions:
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '2m', target: 100 }, // Ramp up to 100 users
{ duration: '5m', target: 100 }, // Hold at 100 users (load test)
{ duration: '2m', target: 500 }, // Ramp to 500 users (stress test begins)
{ duration: '3m', target: 500 }, // Hold at stress level
{ duration: '2m', target: 0 }, // Ramp down
],
thresholds: {
http_req_duration: ['p(95)<500'], // 95th percentile under 500ms
http_req_failed: ['rate<0.01'], // Error rate under 1%
},
};
export default function () {
const response = http.post('https://test.service.govt.nz/api/auth/login',
JSON.stringify({ username: 'test-user', password: 'Test1234!' }),
{ headers: { 'Content-Type': 'application/json' } }
);
check(response, { 'login successful': (r) => r.status === 200 });
sleep(1);
}
Reading the results:
- p95 response time — 95% of requests completed within this time. If p95 > 500ms at 100 users, you have a problem before stress even starts.
- Error rate — any error rate above 1% under normal load is a go/no-go blocker. At stress load, track when it first exceeds 1%.
- Throughput (req/s) — does it plateau or drop as you add users? A plateau means you've hit a ceiling. A drop means the system is struggling.
- Breaking point — the user count at which error rate spikes. This is your capacity ceiling — document it, don't hide it.
5 When to Use Each Type
| Technique | What it tests | NZ example |
|---|---|---|
| Load | Normal peak capacity — can the system handle expected concurrent users? | WINZ online portal at 9am Monday |
| Stress | Breaking point — at what user count does the system fail? | Revenue NZ myIR at tax year-end |
| Soak | Memory leaks and degradation over sustained load (8h+) | Hospital patient management system running overnight |
| Spike | Sudden burst — recovery after a sharp load jump | Revenue NZ site when Budget is announced at 2pm |
| Volume | Large data volumes — not concurrent users but data size | Council rates system processing 250,000 property records |
| Scalability | Does performance improve linearly as infrastructure scales horizontally? | Cloud-hosted service adding Kubernetes pods under load |
6 Common Mistakes
❌ I used to think: if it works for 10 users it'll work for 1,000.
Actually: performance doesn't scale linearly. Database connection pools, caching layers, and thread management often introduce non-linear bottlenecks. A system that handles 10 users with 80ms response time may hit 4 seconds at 200 users if connection pooling is misconfigured.
❌ I used to think: performance testing is the DevOps team's job.
Actually: testers define the test scenarios, acceptance criteria, and interpret results against business requirements. DevOps provisions the infrastructure and monitors it. Neither owns the full picture alone — and when they don't collaborate, you get the Auckland agency story above.
❌ I used to think: p95 response time is the only metric that matters.
Actually: error rate under load matters more than response time. A system responding in 2 seconds with 0% errors is production-ready. A system responding in 400ms but crashing at peak is not. Always set error rate thresholds before response time thresholds in your acceptance criteria.
7 Industry Reality
- Performance test environments are almost never production-equivalent. You'll inherit a test environment with half the CPU, a different database engine version, and no CDN — then be asked to sign off that production will be fine. The honest answer is "we can identify trends and relative changes, but not absolute production numbers." Say it out loud. Document it in your test report.
- Acceptance criteria rarely exist when you arrive. Product owners specify "the system must be fast" and expect you to turn that into a threshold. Push for numbers tied to business impact: "under 2 seconds because users abandon checkout at 3 seconds" or "under 500ms because the SLA with MBIE specifies it." Generic thresholds aren't defensible.
- NZ public sector systems often run on on-premise infrastructure with fixed capacity — no elastic scaling. That makes your breaking point number critically important: when you find it at 600 concurrent users and the real peak is 700, that's a budget conversation, not just a test result. Testers who can translate technical findings into procurement decisions are rare and valued.
- Soak tests are the first casualty of time pressure. Eight-hour tests don't fit into a two-day sprint. In practice, many teams run 90-minute soak tests and flag the limitation. That's better than nothing, but document the risk: a memory leak that takes 6 hours to manifest will still manifest in production.
- You'll find that the bottleneck is almost never where the dev team expected. It's usually an undiscovered N+1 query pattern, a misconfigured connection pool, or a third-party API with rate limiting. Profiling during performance tests — not just watching metrics — is what actually surfaces the fix.
8 When to Use It — and When Not To
✓ Use it when
- The system handles concurrent users or high-volume transactions — any public-facing service, especially NZ government portals with known peak windows (9am Monday, tax year-end, school term enrolments)
- You have an SLA or non-functional requirement with a response time or uptime number attached — something to test against
- A significant architectural change has been made (new database, switched from monolith to microservices, added caching) and you need to verify the change didn't regress performance
- The system is expected to scale horizontally and you need evidence that adding infrastructure actually helps
- There's a planned demand event — a product launch, a public health announcement, an Revenue NZ deadline — where a failure would be high-visibility and politically costly
✗ Skip it when
- It's a low-traffic internal tool used by 5–20 staff — performance testing cost exceeds the risk. Exploratory testing under realistic conditions is enough.
- You have no production-representative environment and no way to get one — the results will mislead rather than inform. Run a quick manual benchmark instead and label it clearly as indicative.
- The system is entirely behind a CDN with static content — performance characteristics are determined by the CDN vendor, not your application code.
- Budget or timeline absolutely prohibits it — but in this case, escalate the risk in writing rather than silently skipping it. The risk doesn't disappear because the test didn't happen.
Context guide
How the right level of performance testing effort changes based on project context.
| Context | Priority | Why |
|---|---|---|
| Revenue NZ myIR or Benefits NZ benefit portal — tax season or benefit payment day | Essential | Concurrent load spikes are predictable and large — hundreds of thousands of NZers hit the system simultaneously. Degraded response times have direct financial and welfare consequences for real people. |
| Pacific Air booking and check-in platform ahead of holiday peak | Essential | Boxing Day and school-holiday sales trigger traffic surges 10–20x normal. A slow or failed booking page costs revenue and damages brand reputation in a highly competitive market. |
| KiwiFirst Bank or Harbour Bank internet banking — steady-state consumer product | High | RBNZ operational resilience requirements and customer SLAs demand sub-second response for balance and transfer screens. Performance regressions in a release will be noticed within hours by a large user base. |
| TransitNZ TransitNZ RealMe-integrated licensing service — moderate daily traffic | High | The integration with RealMe and back-end legacy systems creates latency risks that basic functional tests will not catch. Load testing verifies the integration chain holds under concurrent users, not just the UI. |
| Internal council or government intranet (e.g. Auckland Council staff portal) | Medium | User base is bounded and predictable, so full load testing is usually disproportionate. Baseline smoke tests and response-time checks on the heaviest queries are sufficient unless a major re-platform is planned. |
| Small-to-medium NZ SaaS product with under 500 active users (e.g. early-stage CloudBooks competitor) | Low | Infrastructure auto-scales and current usage is well within capacity. Invest the testing effort in functional and exploratory coverage first — revisit performance testing when user growth or a critical path demands it. |
Trade-offs
What you gain and what you give up when you choose performance testing.
| Advantage | Disadvantage | Use instead when… |
|---|---|---|
| Catches capacity problems before real users do — you find out the Benefits NZ payment portal collapses at 8,000 concurrent sessions in a load test, not on a Monday morning when benefit payments process. | Setting up realistic test data and a representative load profile takes significant time — cloning production-like volumes for an Revenue NZ-scale system can be weeks of environment work before a single test runs. | The system has very few concurrent users and scales horizontally with no predicted spike events — a simple response-time check in your functional test suite is proportionate. |
| Provides objective, repeatable metrics (response time percentiles, throughput, error rate) that give development teams a clear regression signal across releases — "p95 jumped from 420 ms to 1.4 s after the DB schema change." | Results are only as valid as the environment they run in. A test that passes on an under-resourced staging server at Spark or TechServNZ may behave completely differently on the production cluster — environment parity is expensive to maintain. | You cannot get a representative environment and the team does not have budget — APM monitoring in production (e.g. New Relic, Datadog) with alerting on p95 latency is a cheaper first line of defence. |
| Identifies the weakest link across the full stack — load tests routinely expose database connection pool exhaustion, third-party API throttling (e.g. a supplier hitting its RealMe call quota), and misconfigured CDN caching that functional tests never reach. | A passing load test can create false confidence. Synthetic traffic from k6 or JMeter cannot fully replicate the chaos of real NZ users: browser caching behaviour, mobile network retries, and geographic distribution from Auckland to Invercargill all affect real-world results. | You need to understand real user experience rather than synthetic throughput — Real User Monitoring (RUM) or synthetic monitoring from Catchpoint/Pingdom with NZ probe locations gives you ground truth on actual perceived performance. |
| Stress and soak tests expose memory leaks and resource exhaustion that only appear over time — critical for long-running NZ government batch processes (e.g. CoverNZ weekly claims processing jobs) that run for hours unattended. | Performance testing generates a lot of data but finding the root cause of a slowdown still requires developer involvement — testers find the symptom (slow endpoint), but fixing a N+1 query or a missing index requires engineering time the project schedule may not allow. | The bottleneck is already known and localised — targeted profiling of that specific component (e.g. query analysis with EXPLAIN in PostgreSQL, or a JVM heap dump) is faster and more surgical than running a full load test suite. |
Enterprise reality
At 200–300-developer scale, performance testing stops being a single engineer running a script — it becomes a coordinated discipline with dedicated tooling, governance gates, and cross-squad ownership.
- Small teams run k6 or JMeter ad hoc before a release; enterprises run continuous performance pipelines — nightly baseline regressions, automated capacity-planning suites, and canary load tests promoted through environments. At Revenue NZ, the income tax platform runs scheduled soak tests against a production-mirror environment before every major release cycle, because a 10% throughput regression during a filing period is a national incident, not a bug ticket.
- Performance data that touches personal tax or health records must meet Privacy Act 2020 obligations — test data must be synthetic or fully anonymised, and NZISM controls govern where load-test traffic is routed. PCI DSS environments (any squad touching card payment flows at Harbour Bank or Pacific Bank) require performance evidence as part of the audit trail — you cannot just delete those test reports.
- At scale, teams standardise on a performance stack rather than letting squads pick their own: Grafana K6 for scripting, InfluxDB or Prometheus for metrics, Grafana dashboards for results, and a shared Gatling-based regression suite for the critical user journeys. Letting 10 squads each use a different tool means no comparable baselines and no shared alerting thresholds.
- When cross-squad coordination fails, the consequences compound — TeleNZ's digital channels serve millions of concurrent sessions during outage events; if the broadband squad and the mobile squad each assume the other owns the shared API gateway load profile, neither will have tested the combined concurrency scenario. One missed dependency in a shared service becomes a cascading timeout that affects every squad's SLO simultaneously.
◆ What I would do
Professional judgment — when to reach for performance testing, when to skip it, and what to watch for.
The bottom line: Performance testing is not about finding a number to put in a report — it is about knowing your system's breaking point before your users discover it for you. In NZ public-sector and financial services contexts, that breaking point often has a regulatory dimension, not just a UX one. Always tie your pass/fail criteria to a real SLA or published commitment before the test runs, not after you see the results.
9 Best Practices
- ✓ Always establish a baseline before comparing. Run your load test against the current build first. Then run it again after the change. Without a baseline, you can't tell if performance improved, regressed, or stayed the same.
- ✓ Set error rate thresholds before response time thresholds. A system that responds in 400ms but crashes at peak is not production-ready. Error rate < 1% under load is the primary gate; response time is secondary.
- ✓ Use realistic test data, not synthetic. A database with 100 rows behaves very differently from one with 10 million rows. Seed your test environment with a representative volume of data before running any performance test.
- ✓ Monitor infrastructure during the test, not just the application. CPU, memory, disk I/O, and network throughput on the server side are what tell you where the bottleneck lives. Watching only HTTP response times is like diagnosing a car fault by how fast it accelerates.
- ✓ Run performance tests in isolation. A shared environment where another team is running functional tests introduces noise that will make your results unreliable. Book a test window and enforce it.
- ✓ Profile during the stress test, not after. Attach APM tooling (Datadog, New Relic, or even k6's built-in metrics) during the run so you capture which endpoints, queries, or services are degrading in real time.
- ✓ Document the breaking point, don't suppress it. When you find the system falls over at 600 concurrent users and the expected peak is 700, that is a critical finding. Write it clearly in your test report with the exact failure signature. Softening the finding serves no one.
- ✓ Include warm-up time in your test design. Systems with JVM warm-up, cache population, or connection pool initialisation behave poorly for the first 60–120 seconds. Include a ramp-up period and exclude it from your SLA measurement window.
- ✓ Negotiate spike test recovery criteria, not just peak handling. "The system handles 10x load" is incomplete. "The system returns to p95 < 500ms within 2 minutes after the spike subsides" is testable and reflects real user impact.
- ✓ Report results with context, not just numbers. "p95 = 487ms" is less useful than "p95 = 487ms at 200 concurrent users on a 4-vCPU test environment, compared to the 16-vCPU production environment — extrapolation is not reliable."
10 Common Misconceptions
❌ Myth: If the system passed load testing, it's ready for production peak traffic.
Reality: Load testing validates one scenario — normal peak load in a controlled environment. It says nothing about data volume effects, third-party API behaviour under concurrent load, cache cold-start after a deployment, or the behaviour of a connection pool that has been running for 6 hours. Load testing is a necessary condition for production readiness, not a sufficient one. You also need stress, soak, and — for systems with demand events — spike testing.
❌ Myth: Performance testing is about finding the average response time.
Reality: Averages hide the worst user experiences. A system with a 200ms average but 8-second p99 means roughly 1 in 100 requests takes 8 seconds — enough to lose that user. Report and set thresholds on percentiles: p50 (median), p95 (most users), and p99 (tail). For payment or health systems, p99 is your minimum reporting standard. The average is a vanity metric in performance testing.
❌ Myth: Performance testing only matters for high-traffic consumer apps — small NZ business apps don't need it.
Reality: The case for performance testing scales with consequence, not traffic. A council rates system processing 250,000 property records overnight needs volume testing. An emergency services dispatch system with 40 concurrent dispatchers needs load testing. A healthcare system running batch reports at 2am needs soak testing. The question is "what happens if this is slow or fails?" — not "how many users does it have?"
11 Now You Try
Design a performance test strategy for the NZ COVID tracer app equivalent — an app that processes contact tracing check-ins. It normally handles 200,000 check-ins per hour. On a high-alert day, it could receive 10× normal load. Define the test types you'd run, the acceptance criteria for each, and the key risk scenarios.
How this has changed
The field moved. Here is how Performance Testing evolved from its origins to current practice.
Performance testing means running the application on production-scale hardware under simulated load — expensive, slow, and done once before release. LoadRunner (1999) brings load generation into software tooling. Most teams cannot afford the infrastructure or tools.
JMeter open-sourced by Apache. The first widely accessible load testing tool. Teams can now run performance tests without expensive commercial tools. But performance testing is still a pre-release gate, not a continuous practice.
Cloud-based load testing (BlazeMeter, Loader.io) removes the need for load generation infrastructure. Any team can generate global load from multiple regions. k6, Gatling, and Locust offer developer-friendly scripting alternatives to JMeter's XML configuration.
Continuous performance testing enters CI pipelines. Performance budgets and SLO-based pass/fail gates replace manual analysis. The shift from "is it fast enough?" to "is it faster or slower than yesterday?" changes how performance is monitored.
AI tools can predict performance degradation from code changes before load tests run, using historical performance data and code complexity metrics. Chaos engineering extends performance testing to resilience under failure. NZ financial services and government systems must demonstrate performance under peak load (e.g., tax filing periods, benefit payment days) as part of operational resilience evidence.
12 Self-Check
Click each question to reveal the answer.
Why teams fail here
- Load profiles are based on guesses rather than production traffic analytics or business forecasts
- Tests run against an under-resourced test environment that does not reflect production sizing
- Results show the system handles X users without defining what handles means in terms of response time or error rate
- Performance testing happens once before go-live, not continuously as code and data volumes change
Interview Questions
What NZ hiring managers ask about Performance Testing — and what strong answers look like.
What is the difference between load testing, stress testing, and soak testing?
Strong answer: Load testing verifies the system performs within SLOs at expected production volumes — confirm that 500 concurrent users get sub-200ms response times. Stress testing pushes beyond expected volumes to find the breaking point and verify graceful degradation — at what load does performance degrade significantly, and does the system recover when load drops? Soak testing runs at normal load for an extended period (hours to days) to detect memory leaks, connection pool exhaustion, and gradual degradation that only appears over time. For a NZ government system processing benefit payments, I would run soak testing before monthly payment runs where sustained high volume is expected.
Junior/Mid
k6 shows your API has a p99 response time of 2.1 seconds under load. Your SLO is 2 seconds. How do you proceed?
Strong answer: I distinguish between a measurement issue and a real violation. First, I verify the test used a realistic ramp-up (sudden load is not representative), warm-up period (first requests are always slower due to JIT compilation and connection pool fill), and realistic think time between requests. If the test is realistic and p99 is 2.1 seconds, I am 1% of requests over SLO — technically failing but marginally. I look at whether the slow requests correlate with specific endpoints, database queries, or external calls. I share the result with the product team as a data point: is 2 seconds a hard customer commitment or a target? Depending on the answer, we either investigate the bottleneck or adjust the SLO with stakeholder agreement.
Mid/Senior
What would you instrument to diagnose a performance regression between two releases?
Strong answer: I start with the comparison: which endpoints regressed, by how much, and at what percentile (p50, p95, p99)? Then I correlate with code changes — which database queries were added or modified? Are there new N+1 patterns? Did any new dependencies add latency? I use distributed tracing (Jaeger, Zipkin, or Datadog APM) to see the breakdown of time within a single request — database time, external call time, serialisation time. I look at infrastructure metrics (CPU, memory, database connection count) under load to identify resource contention. For NZ payment systems, I pay particular attention to external call latency to bank gateways and Revenue NZ APIs, which are outside our control.
Senior/Lead
Q1: What is the key difference between load testing and stress testing?
Load testing verifies the system handles the expected normal peak load within acceptable performance thresholds. Stress testing deliberately exceeds that load to find the breaking point — where the system first fails or degrades unacceptably. You run load before stress.
Q2: Why is soak testing necessary even when load tests pass?
Soak testing exposes memory leaks, connection pool exhaustion, and gradual degradation that only appear over hours or days of sustained load. A system can pass a 30-minute load test and still fail after 8 hours in production because a memory leak slowly consumes available resources.
Q3: You're testing a NZ government tax portal. What performance test type should you prioritise for Budget Day, and why?
Spike testing — Budget Day is a predictable demand event where load jumps suddenly and sharply when the Budget is announced at 2pm. You need to verify the system recovers after the spike, not just that it handles sustained load. Also check how quickly it returns to normal response times after the spike subsides.
Q4: Your team is testing a new Benefits NZ (Work and Income) online case management portal used by 800 case workers across NZ during business hours. The system is on-premise with fixed capacity. Which performance test type should you run first, and what acceptance criteria would you propose?
A: Load testing first — confirm the system handles the expected 800 concurrent case workers within SLA thresholds before anything else. Because capacity is fixed (no elastic scaling), the breaking point is critical: propose measurable criteria such as p95 response time under 1 second and error rate below 0.5% at 800 concurrent users. Document the breaking point explicitly so leadership understands the exact headroom — if it fails at 950 users and Benefits NZ expands the workforce, that is a procurement decision, not just a test finding.
Q5: What is the key difference between volume testing and load testing, and when would you choose volume testing over load testing for an CoverNZ claims processing system?
A: Load testing measures the system's behaviour under concurrent users — how many people can use it at once. Volume testing measures behaviour under large data quantities — how the system performs when processing or querying a very large dataset, regardless of how many users are active. For an CoverNZ claims system, you would choose volume testing when the concern is processing hundreds of thousands of historical claim records in a batch run — for example, overnight reporting across 20 years of claims — rather than simultaneous user activity. Both may be needed, but they answer different questions.
Q6: When would you explicitly recommend skipping formal performance testing, and how would you handle that decision professionally?
A: Skip formal performance testing when the system is a low-traffic internal tool used by fewer than 20 people and the cost of the test exceeds the risk of slow performance. Also skip it when you cannot obtain a production-representative environment, because results will mislead rather than inform. In both cases, the professional response is not to silently skip it — escalate the risk in writing. Document "performance testing was not conducted due to [reason]; the risk is [specific failure scenario]; the business accepts this risk" and get a sign-off. The risk doesn't disappear because the test didn't happen.
Q7: A developer tells you "our load test passed with 300ms average response time, so we're good to go live." What is wrong with this reasoning and how do you respond?
A: Two problems. First, averages hide tail latency — a 300ms average can coexist with a p99 of 8 seconds, meaning roughly 1 in 100 users waits 8 seconds. For a service like RealMe identity verification or an Revenue NZ payment, that tail experience matters. Second, passing load testing is necessary but not sufficient: it says nothing about memory leaks under sustained load, behaviour after a spike, or data volume effects. Respond by asking for the p95 and p99 numbers, the error rate under peak load, and whether soak and stress tests were run. Redirect the conversation from "did it pass?" to "what failure modes have we eliminated?"
13 ISTQB Mapping
ISTQB CTAL-TA v3.1.2, Section 3.3.2 — Performance testing techniques: load, stress, soak, spike, volume, and scalability. Testers at this level are expected to select the appropriate technique based on the quality risk and define measurable acceptance criteria.
CTFL v4.0, Section 4.1.1 — Non-functional characteristics including performance efficiency as a testable quality characteristic.
15 Performance Test Types Deep-Dive
Each performance test type targets a different failure mode. Understanding the nuance between them lets you specify the right test — and defend the choice to stakeholders who think "load testing" covers everything.
Load Testing
Validates the system handles the expected number of concurrent users within agreed performance thresholds. This is your baseline — run it first, before anything else. If the system fails load testing, there is no point running stress or soak.
Stress Testing
Deliberately exceeds expected load to find the breaking point — the user count or transaction rate at which the system first fails or degrades unacceptably. The goal is not to make the system fail for fun; it is to know the failure mode before production discovers it.
A system that fails gracefully at 800 users (returns a "service busy" message and queues requests) is far safer than one that silently corrupts data at 750 users. Stress testing tells you how it fails, not just when.
Spike Testing
Simulates a sudden, sharp traffic surge — not a gradual ramp, but a near-instantaneous jump from normal to peak and back. The key metric is not just whether the system survives the spike, but how quickly it recovers to normal response times after the spike subsides.
Soak / Endurance Testing
Runs the system at moderate sustained load for 12–24+ hours to expose problems that only emerge over time: memory leaks, connection pool exhaustion, disk filling, gradual thread accumulation. A system can pass a 30-minute load test and still fail catastrophically after 8 hours in production.
❌ Common shortcut
Time-pressured teams run 90-minute soak tests. That is better than nothing — but document the limitation explicitly. A memory leak that takes 6 hours to manifest will still manifest in production on night one.
Volume Testing
Tests the system's behaviour under large data sets, independent of concurrent users. The question is not "how many people are using it" but "how does it perform when the database holds millions of records and the batch job must process all of them?"
16 k6 Quick Start
k6 is the industry-standard open-source performance testing tool for APIs and web services. Scripts are written in JavaScript, version-controlled alongside your codebase, and CI-ready. Here is a complete script for a NZ KiwiSaver balance API covering ramp-up, sustained load, spike, and ramp-down — with thresholds that act as automated pass/fail gates.
import http from 'k6/http';
import { check, sleep } from 'k6';
// Performance thresholds — the test FAILS if these are breached
export const options = {
stages: [
{ duration: '2m', target: 50 }, // Ramp up to 50 virtual users
{ duration: '5m', target: 50 }, // Hold at 50 — normal load window
{ duration: '1m', target: 200 }, // Spike to 200 users (end-of-financial-year burst)
{ duration: '5m', target: 200 }, // Hold at spike level
{ duration: '2m', target: 0 }, // Ramp down — verify graceful recovery
],
thresholds: {
// 95th-percentile response time must stay under 2 seconds
http_req_duration: ['p(95)<2000'],
// Error rate must stay under 1%
http_req_failed: ['rate<0.01'],
},
};
export default function () {
const params = {
headers: {
'Content-Type': 'application/json',
// Use a real member token in your .env — never hardcode production credentials
'Authorization': 'Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.test-member-token',
},
};
const response = http.get(
'https://test-api.kiwisaver-provider.co.nz/v2/members/balance',
params
);
// Assertions — failures increment the http_req_failed metric
check(response, {
'status is 200': (r) => r.status === 200,
'response under 2 seconds': (r) => r.timings.duration < 2000,
'balance present in body': (r) => JSON.parse(r.body).balance !== undefined,
});
sleep(1); // 1-second think time between requests (simulates real user pacing)
}
Reading the output:
- http_req_duration p(95) — if this exceeds 2000ms at any stage, the threshold fails and k6 exits with a non-zero code (CI pipeline fails automatically).
- http_req_failed rate — any value above 0.01 (1%) is a threshold breach. Monitor this at the spike stage — it is often the first metric to move.
- Spike recovery — watch p95 in the final ramp-down stage. If it stays elevated after load drops, the system is not recovering cleanly (connection pool starvation or GC pressure).
k6 run kiwisaver-balance.js to your pipeline. k6 returns exit code 1 on threshold breach — your pipeline treats it as a failed build, same as a failing unit test.17 Performance Targets for NZ Systems
Generic targets like "under 3 seconds" are hard to defend. These reference targets are grounded in WCAG 2.1 timing guidance and NZ Government Digital Service Standards, giving you a starting point for negotiating acceptance criteria with product owners and stakeholders.
| System Type | P95 Target | Error Rate | Reasoning |
|---|---|---|---|
| Consumer web KiwiSaver portals, MyIR |
< 2s | < 0.1% | Research shows user abandonment rises sharply above 3s; 2s leaves headroom. Financial services demand near-zero errors. |
| Government portals RealMe, SmartStart, Apply |
< 3s | < 0.5% | NZ DIA Digital Service Standard mandates accessible, responsive public services. 3s aligns with WCAG 2.1 timing guidance for cognitive load. |
| Internal tools HRIS, case management, CRM |
< 5s | < 1% | Staff users tolerate slower response times than consumers, and traffic is predictable. Still set a threshold — "slow" without a number is not testable. |
| APIs Partner integrations, open data |
< 500ms | < 0.1% | APIs are called programmatically in chains — a 2s API response compounds across a multi-step workflow. Machine-to-machine calls warrant tighter latency targets. |
| Batch processing Tax runs, payroll, KiwiSaver reconciliation |
Throughput over latency | < 0.01% | Batch jobs are judged by total window completion time and error-free record count, not individual request latency. A single corrupted tax record matters more than a 10s job step. |
If a product owner pushes back on a 2s target as too strict, ask: "What is the business cost of a user abandoning the transaction?" For Revenue NZ MyIR, an abandoned tax filing is a support call and a compliance risk. That framing converts a technical argument into a business one — and usually wins.
14 Next Steps
Prerequisites
Related Techniques
What to Learn Next
Also in Bootcamp
↑ Go Deeper
This technique is foundational. Once you understand it, these specialised tracks take you into real-world depth: