Load testing — smoke/load/stress/soak/spike profiles, SLO thresholds, tooling.
Scanned 9/6/2026
Install to Claude Code
npx -y skills add Dev-Toolbelt/dev-team-agents --skill load-testing --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Load Testing?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/dev-toolbelt-load-testing)More formats (shields.io, HTML) on the badges page.
---
name: load-testing
description: Load testing — smoke/load/stress/soak/spike profiles, SLO thresholds, tooling.
---
# Load Testing
Server-side performance validation: how much traffic the system takes before it degrades, and where it breaks first.
## When to Load
Load for backend, API, or infrastructure work where capacity, concurrency or throughput matter — before a launch, before a traffic event, after an architecture change, or when investigating production saturation.
**Boundary with `skills/architecture/performance-budgets/SKILL.md`:**
| Concern | Skill |
|---|---|
| Client-side rendering, Core Web Vitals, bundle size, Lighthouse CI | `performance-budgets` |
| Server latency under concurrency, throughput, saturation, breaking point | this skill |
They are complementary: a page can hit every Core Web Vitals target at one user and still collapse at a thousand.
---
## Load Profiles
Pick the profile from the question being asked. Running the wrong profile answers a question nobody had.
| Profile | Shape | Question answered | Typical duration |
|---|---|---|---|
| **Smoke** | Minimal load, 1–5 virtual users | Does the script work and the system respond correctly at all? | 1–2 min |
| **Load** | Ramp to expected peak, hold | Does the system meet its SLOs at normal peak traffic? | 10–30 min |
| **Stress** | Ramp past peak until degradation | Where is the breaking point, and what breaks first? | 20–60 min |
| **Soak** (endurance) | Moderate load held for hours | Do leaks, unbounded caches, connection or disk growth appear over time? | 2–24 h |
| **Spike** | Instant jump to a multiple of peak, then drop | Does the system survive a sudden surge and **recover** afterwards? | 5–15 min |
| **Breakpoint** | Slow unbounded ramp | What is the maximum sustainable throughput per instance? | Until failure |
Rules:
- **Always run smoke first.** Most "failed" load tests are broken scripts, wrong credentials, or a 404 loop returning fast errors.
- Ramp gradually in load and stress tests — instant ramps only measure cold starts and connection pool warmup.
- Include a **recovery window** after spike and stress runs; a system that never recovers without a restart has a worse defect than the one that slowed down.
- Change one variable per run. A test that changes load *and* configuration explains nothing.
---
## Deriving Thresholds from SLOs
Thresholds are not invented for the test — they come from the service's SLOs (`skills/architecture/observability-slo/SKILL.md`). If no SLO exists, defining one is the first deliverable, not the load test.
1. Take the user-facing SLO (e.g. *99% of checkout requests complete under 800 ms*).
2. Convert to a **percentile threshold with a load level**: `p99 < 800ms at 500 concurrent users`.
3. Set the error-rate threshold **below** the error budget, not at it — the test must fail before users would notice.
4. Add a throughput floor: sustained requests/second the system must handle without breaching the above.
5. Encode all of them as **pass/fail assertions in the tool**, so the run exits non-zero on breach. A load test with no assertions is a graph nobody reads.
| Traffic input | Source |
|---|---|
| Expected peak concurrency | Production analytics — peak hour, not daily average |
| Request mix | Real endpoint distribution from access logs or APM, not an even split |
| Growth headroom | Test at 2× expected peak; capacity planning targets, not wishes |
---
## What to Measure
| Metric | Rule |
|---|---|
| **Latency percentiles** (p50, p90, p95, p99) | Report percentiles, never averages — an average hides the tail that defines user experience |
| **Throughput** (req/s, completed iterations/s) | The real capacity number; must be reported alongside the concurrency that produced it |
| **Error rate** by status and type | Separate 4xx (script/data problem) from 5xx and timeouts (system problem) |
| **Saturation** | CPU, memory, connection pool usage, queue depth, thread pool, disk I/O, file descriptors |
| **Downstream** | Database slow queries, lock waits, cache hit ratio, external API latency |
| **Client-side saturation** | Load generator CPU/network — a saturated generator invents latency that does not exist |
Interpretation rules:
- A rising p99 with flat p50 means **queuing or contention**, not slow code.
- Throughput plateauing while latency climbs marks the **saturation point** — the breaking point is just after it.
- Errors appearing before latency degrades usually means a hard limit (connection pool, rate limiter, file descriptors), not capacity.
- Correlate every run with server-side observability. Numbers from the load generator alone cannot tell you *why*.
---
## Test Data and Environment Pitfalls
| Pitfall | Consequence | Mitigation |
|---|---|---|
| Same user/record for every virtual user | Unrealistic cache hits and row-level lock contention | Parameterize with a realistic data pool |
| Dataset far smaller than production | Every query hits an in-memory index; results are meaningless | Seed representative volume and cardinality |
| Environment scaled down from production | Results cannot be extrapolated linearly | Match topology, or state the scaling factor explicitly and never extrapolate silently |
| Autoscaling enabled without noting it | Measures the scaler's reaction time, not the system's capacity | Test fixed capacity first, then autoscaling behavior separately |
| Caches/CDN in front absorbing the load | Tests the cache, not the origin | Bypass or vary keys deliberately; test both paths |
| Rate limiters or WAF throttling the generator | 429s reported as system failure | Allowlist the generator, or make throttling part of the scenario |
| Third-party APIs called for real | Cost, bans, and someone else's latency in the results | Stub with realistic latency and error injection |
| Test writes into shared/production data | Data corruption and contaminated analytics | Dedicated tenant/dataset, cleanup routine, never load-test production without written approval |
| Metrics polluting production dashboards | False alerts and paged on-call | Tag all synthetic traffic with a header and exclude it from alerts |
Also budget for **think time** between requests — real users pause. A no-pause script produces a load pattern that no real population creates.
---
## Tool Selection
Gate on the signals already present in the project — a tool the team can read and maintain beats a marginally faster one.
| Signal | Tool |
|---|---|
| JS/TS codebase, CI-first, scriptable scenarios, thresholds as code | `k6` |
| Python codebase, custom logic per user, distributed generators | `Locust` |
| Java/JVM shop, GUI-driven test design, protocol variety (JMS, JDBC, LDAP) | `JMeter` |
| Scala/Java, high per-node throughput, HTML reports | `Gatling` |
| Quick sanity check on a single endpoint | `hey`, `wrk`, `oha`, `autocannon` |
| gRPC services | `ghz` |
| Cloud-managed distributed runs, no generator infrastructure to operate | Managed service from the existing cloud provider |
| Browser-level load (real rendering, JS execution) | Browser-driver mode of the chosen tool — expensive, use only when protocol-level load cannot answer the question |
Rules:
- Store scripts in the repo next to the code they exercise, reviewed like tests.
- Run smoke profiles in CI on every PR; run heavier profiles on a schedule or before releases — a 30-minute soak does not belong in the PR pipeline.
- Record results as artifacts and track them over time; a single run has no trend and therefore no verdict.
- Test through the same entry point real traffic uses (gateway, load balancer, TLS) — bypassing it removes the layers that usually fail first.
---
## Reporting a Run
Every result must state, in one place:
- Profile and duration, target environment and its capacity
- Concurrency/arrival rate and the request mix
- Throughput achieved, latency percentiles, error rate by type
- Saturation of the top resource and what limited it
- Pass/fail against each declared threshold
- The single next bottleneck to address
"It handled the load" without the numbers above is not a result.
---
## Decision Checklist
- [ ] Is there a stated question, and does the chosen profile answer it?
- [ ] Do thresholds derive from an SLO rather than from intuition?
- [ ] Are latency results reported as percentiles, with the concurrency that produced them?
- [ ] Did a smoke run validate the script before the real run?
- [ ] Is test data parameterized and the dataset representative in volume?
- [ ] Is the environment's relationship to production documented?
- [ ] Are caches, autoscaling, rate limiters and third parties accounted for deliberately?
- [ ] Is server-side saturation captured alongside client-side latency?
- [ ] Are thresholds encoded as assertions so the run fails automatically?
- [ ] Is synthetic traffic tagged and excluded from production alerting?
- [ ] Is the result stored for trend comparison against previous runs?
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!