Performance & Load Testing
Measure and validate system performance with k6 - load tests, stress tests, performance thresholds, and identifying bottlenecks under realistic traffic.
skill: Performance & Load Testing category: Engineering tags: [Performance Testing, k6, Load Testing, Stress Testing, Web Vitals] readingTime: 4
Performance testing prevents embarrassing outages on launch day. The goal isn't maximum load - it's understanding where your system degrades. Run load tests in staging with production-like data and realistic user behavior. The worst load test is one that doesn't match how real users use the system.
Workflow
- Design realistic load scenariosLoad tests that hit one endpoint in a loop don't reflect real user behavior. Realistic scenarios mix read/write operations, include think time, and distribute traffic across endpoints proportionally to production.
- Interpret k6 results and identify bottlenecksRaw metrics (requests/second, latency percentiles) tell you what's slow but not why. Pairing k6 output with APM (Datadog, New Relic) or profiling identifies the bottleneck.
- Run stress and spike testsLoad tests verify normal capacity; stress tests find the breaking point; spike tests verify the system handles sudden traffic bursts and recovers gracefully.
- Profile frontend with Core Web VitalsFrontend performance directly affects conversion rates. Google's Core Web Vitals (LCP, CLS, INP) are the standardized metrics for user experience.
- Integrate performance tests in CIPerformance regressions are invisible without automated checks. CI integration fails the build when p95 latency or error rate exceeds thresholds - before code reaches production.
Output Format
Performance: k6 stages ramp-up/hold/spike with thresholds (p95<500ms, error_rate<1%), stress test doubling VUs to find break point, web-vitals LCP/INP/CLS measurement, and Lighthouse CI budget enforcement.
k6 stages: ramp to 50 VU → hold 5m → spike to 200 → ramp down
thresholds: { "http_req_duration": ["p(95)<500"], "http_req_failed": ["rate<0.01"] }
onLCP/onINP/onCLS + sendToAnalytics for real-user monitoring
Lighthouse CI: minScore:0.9 performance, maxLCP:2500ms
GitHub Action: k6-action → exits 1 on threshold breach → PR failsExamples
Edge Cases & Gotchas
- Test data exhaustion - Load tests that create orders exhaust test inventory or generate gigabytes of data. Use a cleanup script after load tests, or design tests to read-only existing data for sustained tests.
- DNS caching in k6 - k6 caches DNS by default - tests that should test DNS failover need dns: { ttl: "0s" } in options. Also: test from same region as production users for realistic latency.
- HTTP/2 push-down - k6 supports HTTP/2 by default. If your server behaves differently under HTTP/1.1 vs HTTP/2, test both: options: { httpDebug: "true" } to see protocol used.
- Correlation between VU count and real user count - k6 VUs are persistent connections with no think time by default. 100 VUs ≠ 100 concurrent users. With 2s think time per request, 100 VUs ≈ 50 requests/second, not 100.
When to Break These Rules
- Use Gatling instead of k6 for teams with Java expertise - Gatling's Scala/Java DSL integrates naturally with JVM-based test suites and has better HTTP/2 and WebSocket support.
- Use distributed load testing (k6 cloud, Locust on Kubernetes) instead of local k6 when traffic needs to simulate global distribution or exceed 50K RPS from a single machine.
Reference Files
https://k6.io/docs/https://web.dev/vitals/