Load testing answers the question every developer dreads: "Will my app handle the traffic?" Whether it is a product launch, Black Friday, or a Hacker News front page moment, load testing validates that your system won't fall over under pressure. This guide covers load testing tools, test design, and interpreting results to find bottlenecks before your users do.
Load Testing Tools Compared
| Tool | Language | Scripting | Best For | Pricing |
| k6 (Grafana) | Go (JS scripting) | JavaScript (ES6) | Developer-friendly, CI-integrated load tests | Free (OSS), $99/mo Cloud |
| Artillery | Node.js | YAML + JS hooks | HTTP + WebSocket + Socket.io testing | Free (OSS), $150/mo Pro |
| Locust | Python | Python | Python-native, distributed by design | Free (OSS) |
| wrk2 | C | Lua scripting | Maximum raw throughput, micro-benchmarks | Free (OSS) |
| Vegeta | Go | CLI + targets file | Simple HTTP load generation, pipelining | Free (OSS) |
| Hey | Go | CLI only | Quick one-liner load tests | Free (OSS) |
Designing a Realistic Load Test
| Element | Bad (Unrealistic) | Good (Realistic) |
| Test Data | One user ID repeated 10,000 times | 10,000 unique user IDs with realistic distribution |
| Request Paths | 100% on /api/health (simple endpoint) | Traffic distributed across real user paths: 60% browse, 20% search, 15% detail, 5% checkout |
| Think Time | 0ms between requests (robot behavior) | 1-5s pauses between actions (human behavior) |
| Ramp Pattern | Instant 10,000 concurrent users | Ramp from 0 → 1,000 → 5,000 → 10,000 over 10 minutes |
| Assertions | Only check HTTP 200 | Check: status code, response time < 200ms, body contains expected data, no errors in response |
Key Load Testing Metrics
| Metric | What It Means | Red Flag |
| P50 (Median) Latency | Half of requests are faster than this | P50 > 200ms for API endpoint |
| P95 Latency | 95% of requests are faster than this | P95 > 500ms for user-facing API |
| P99 Latency | 99% tail latency — worst-case users | P99 > 1s for any endpoint |
| Throughput (RPS) | Requests per second the system can handle | Throughput plateaus while RPS increases (saturation) |
| Error Rate | % of requests that fail | >0.1% for production-critical paths |
| Concurrent Users | Simultaneous virtual users | System crashes before reaching target concurrency |
k6 Example: Production-Ready Load Test
// load-test.js — realistic e-commerce load test
import http from 'k6/http';
import { check, sleep, group } from 'k6';
import { Rate, Trend } from 'k6/metrics';
const errorRate = new Rate('errors');
const checkoutTime = new Trend('checkout_time');
export const options = {
stages: [
{ duration: '2m', target: 100 }, // Ramp to 100 users
{ duration: '5m', target: 500 }, // Hold at 500
{ duration: '3m', target: 1000 }, // Ramp to 1000
{ duration: '5m', target: 1000 }, // Hold at peak
{ duration: '2m', target: 0 }, // Ramp down
],
thresholds: {
http_req_duration: ['p(95)<500'], // 95% of requests < 500ms
errors: ['rate<0.01'], // Error rate < 1%
},
};
export default function () {
// Browse products
const browse = http.get('https://api.example.com/products?page=1');
check(browse, { 'browse OK': (r) => r.status === 200 });
sleep(2);
// Search
const search = http.get('https://api.example.com/search?q=laptop');
check(search, { 'search OK': (r) => r.status === 200 });
sleep(1);
// View product detail (10% of users)
if (Math.random() < 0.1) {
const detail = http.get('https://api.example.com/products/42');
check(detail, { 'detail OK': (r) => r.status === 200 });
sleep(1);
}
// Checkout (2% of users)
if (Math.random() < 0.02) {
const checkout = http.post('https://api.example.com/checkout', JSON.stringify({
product_id: 42, quantity: 1
}), { headers: { 'Content-Type': 'application/json' } });
check(checkout, { 'checkout OK': (r) => r.status === 201 });
checkoutTime.add(checkout.timings.duration);
}
}
Bottom line: k6 is the best load testing tool for developers — JavaScript scripting, great CI integration (GitHub Actions, GitLab CI), and built-in metric analysis. The key to useful load tests: use realistic user behavior (varying paths, think times, data), test at your expected peak traffic + 50% headroom, and integrate into CI so you catch performance regressions before they ship. Run small tests frequently (every PR) and large tests before major events. See also: Rate Limiting Strategies and PostgreSQL Query Optimization.