Common pitfalls and anti-patterns
Even experienced teams make load testing mistakes that render their results useless or actively misleading. This page catalogs the most common anti-patterns, explains why they produce bad results, and shows what to do instead in a MaxoPerf workflow.
Anti-pattern 1: Testing in production without authorization or coordination
Section titled “Anti-pattern 1: Testing in production without authorization or coordination”What it looks like: Running a load test directly against a production URL without telling your operations team, without a maintenance window, and without confirming you are authorized to generate that load.
Why it is harmful: A load test at scale is indistinguishable from a DDoS attack from your system’s perspective. It can cause real outages for real users, trigger rate-limiter blocks that affect production traffic, exhaust connection pools, and generate billing charges (cloud auto-scaling, CDN egress, SMS/email notifications).
What to do instead: Always test in a staging or dedicated performance environment. If you must test in production, coordinate with your SRE team, set a maintenance window, and confirm explicit authorization. See Safe and ethical load testing.
Anti-pattern 2: The “ramp to infinity” test mistaken for a load test
Section titled “Anti-pattern 2: The “ramp to infinity” test mistaken for a load test”What it looks like: Setting VUs to the maximum your account allows and declaring the test a “load test”. No target throughput, no steady-state window, no SLO to measure against.
Why it is harmful: This is a breakpoint/capacity test, not a load test. It tells you where your system breaks, not whether it meets your SLO at your expected peak. Using it as a load test means you have no benchmark for “passing”.
What to do instead: Define a specific target load (e.g. 500 RPS, 200 concurrent VUs) derived from your production traffic. Run a load test at that target with a stability window. Use failure criteria to encode your SLO as a pass/fail gate.
Anti-pattern 3: Measuring only the ramp-up period
Section titled “Anti-pattern 3: Measuring only the ramp-up period”What it looks like: A 5-minute test with a 5-minute ramp-up. All measurement happens while VUs are still climbing — no steady-state window.
Why it is harmful: During ramp-up, JVM warm-up, DNS caching, connection pool growth, and CDN priming are all happening simultaneously. Latency during this phase is biased high relative to steady-state, and throughput is artificially low.
What to do instead: Design your test as: ramp-up period + steady-state window + optional ramp-down. Measure only the steady-state window. A common pattern:
execution: - ramp-up: 3m hold-for: 10m # measure this window ramp-down: 2m concurrency: 200Anti-pattern 4: Single-user, single-path testing
Section titled “Anti-pattern 4: Single-user, single-path testing”What it looks like: 100 VUs all hitting GET /api/products/42 in an infinite loop.
Why it is harmful: The same resource ID gets cached in every layer (CDN, app cache, DB query cache). Your results reflect cache-hit performance, not real-user performance. The product team ships the feature, the cache warms in production, and the first uncached request is 10× slower than your test showed.
What to do instead: Use a CSV data entity to vary the resource ID (and user session) per VU. Reflect your real traffic mix — multiple endpoints, multiple user paths. See Test data dos and don’ts and Designing realistic load.
Anti-pattern 5: Ignoring runner saturation
Section titled “Anti-pattern 5: Ignoring runner saturation”What it looks like: Pushing 2,000 VUs across 2 runners and reporting the p99 latency as an application metric.
Why it is harmful: Each MaxoPerf runner has a recommended VU capacity. When a runner is saturated, it introduces request queuing and latency inflation at the load-generation layer — before the request even reaches your application. Your results reflect runner bottleneck, not application bottleneck.
What to do instead: Check the Runners tab during every run. If any runner shows a degraded or warning state, or if p99 climbs without a matching error rate increase, suspect runner saturation. Re-run with more runners (or lower VUs per runner) and compare results.
Anti-pattern 6: The “passed last month, must still pass” assumption
Section titled “Anti-pattern 6: The “passed last month, must still pass” assumption”What it looks like: A load test was run 6 months ago, it passed, so the team skips load testing for the next 10 releases.
Why it is harmful: Every release changes the system. Database schemas change, new middleware is added, third-party dependencies change their performance characteristics. A 6-month-old load test result is not a proxy for current system behaviour.
What to do instead: Run baseline regression tests as part of your CI pipeline using MaxoPerf’s scheduled tests and failure criteria. A regression test run on every deploy takes minutes and catches performance regressions before they reach production.
Anti-pattern 7: Averaging percentiles across runs
Section titled “Anti-pattern 7: Averaging percentiles across runs”What it looks like: Running three load tests and averaging the p99 latency across all three to get a “reliable” p99.
Why it is harmful: Averaging percentiles is mathematically incorrect. The p99 of [p99_run1, p99_run2, p99_run3] is not the p99 of the combined distribution. You end up with a number that is neither meaningful nor reproducible.
What to do instead: Use the comparison view in MaxoPerf to compare individual runs directly. If you need to summarize multiple runs, compare their p99 values individually and note the range (min, max) rather than averaging them.
Anti-pattern 8: Load testing without failure criteria
Section titled “Anti-pattern 8: Load testing without failure criteria”What it looks like: Running a test, eyeballing the charts, and deciding it “looks fine”.
Why it is harmful: Human judgment on “looks fine” is inconsistent between reviewers, degrades under time pressure, and cannot be automated in a CI pipeline. Teams ship regressions because the chart “looked fine” compared to the last run they remembered.
What to do instead: Define failure criteria for every test — at minimum:
p95(http_req_duration) < <your SLO threshold>rate(errors) < 0.01(1 % error budget)
MaxoPerf evaluates these after the run and sets the run status to Failed if any criterion is violated. This gives you a deterministic pass/fail signal your CI pipeline can act on.
Anti-pattern 9: Not waiting for the system to stabilize before the test
Section titled “Anti-pattern 9: Not waiting for the system to stabilize before the test”What it looks like: Deploying a new version and running a load test immediately — before the application has finished warming its connection pool, loading its caches, or completing its startup routines.
Why it is harmful: Your first-minute results include cold-start artefacts that inflate latency and deflate throughput. You may declare a regression that does not exist in steady-state.
What to do instead: Wait 2–5 minutes after deployment before starting a load test. Add a short warm-up script (a smoke test at low VUs) as the first step in your CI pipeline before the full load run.
Anti-pattern 10: Treating a failed smoke test as a passing load test
Section titled “Anti-pattern 10: Treating a failed smoke test as a passing load test”What it looks like: The smoke test (5 VUs, 60 seconds) shows 5 % errors, but the team decides to run the full load test anyway and reports the load test result.
Why it is harmful: If your system is already erroring at 5 VUs, the errors at 500 VUs are noise — they tell you nothing about load-induced failure modes. You have a functional bug that must be fixed first.
What to do instead: Make a passing smoke test a hard gate before any load test. In MaxoPerf, use the pre-flight checklist and confirm smoke-test green before proceeding.
Quick-reference table
Section titled “Quick-reference table”| Anti-pattern | Root cause | Fix |
|---|---|---|
| Testing prod without authorization | No process | Dedicated perf environment + SRE coordination |
| Ramp-to-infinity as load test | No target defined | Define VU/RPS target from production traffic |
| Measuring only ramp-up | Test design | Add steady-state hold period |
| Single path / single ID | No data variety | CSV data entity with per-VU variation |
| Runner saturation ignored | No Runners tab check | Monitor Runners tab; adjust VU/runner ratio |
| Stale results trusted | No regression cadence | CI-gated scheduled baseline tests |
| Averaged percentiles | Misunderstanding of stats | Compare runs individually |
| No failure criteria | Manual review only | Set p95 + error-rate criteria on every test |
| Cold-start artefacts | Premature test start | Warm up with smoke test before load run |
| Load test after failing smoke | Wrong gate order | Smoke gate → load test |
Where to go next
Section titled “Where to go next”- Pre-flight checklist — run this before every test
- Safe and ethical load testing — authorized testing
- Failure criteria pass/fail gates — SLO gates in CI
- Interpreting results dos and don’ts — reading results correctly