Performance · Senior & Test Lead

k6

Modern load and performance testing from Grafana. Scripts in JavaScript, thresholds in code, giving you automated performance gates on every deploy.

Senior Test Lead ~12 min read

1 The Hook — The NZ Ticket Crash

A popular NZ concert tour went on sale on a Tuesday morning. 15,000 fans hit the site at exactly 9:00 AM. The site, which usually handled 100 users at a time, crashed instantly. The database locked up, the frontend threw "504 Gateway Timeout" errors, and thousands of frustrated fans vented on social media. The company lost tens of thousands in potential sales and significant brand reputation.

The team thought they were ready because the site "felt fast" during testing. But they hadn't performed a Spike Test. With k6, they could have simulated 15,000 concurrent users hitting the site in 30 seconds, identified the database bottleneck, and scaled their infrastructure before the tickets went on sale.

2 The Rule — Performance Budgets (Thresholds)

Performance is a feature. If it's not measured with automated thresholds, it will eventually degrade.

Don't just run a load test and look at the graph. Define Thresholds in your code: "95% of requests must be under 500ms." If the code gets slower, the test fails, and the build breaks.

3 The Analogy — The Bridge Stress Test

Analogy

The Bridge Weight Limit.

Building a software system is like building a bridge. Functional testing (Selenium/Playwright) checks if the bridge stays up when one car crosses it. Performance testing (k6) checks if the bridge stays up when 1,000 cars try to cross it at the same time during rush hour. You don't want to find out the weight limit when the bridge is already full of people.

Senior engineer insight

The most valuable shift in how I think about k6 is treating thresholds as SLOs first, scripts second. When a team writes thresholds that genuinely reflect their uptime commitments — say, p99 < 800 ms on a payment endpoint because that is what the SLA with the bank actually says — the test becomes a contract, not a vanity metric. I have seen teams tune their p95 threshold until the test passes rather than fixing the slow query, which is a red flag that the threshold has no business meaning behind it.

Most common mistake: setting thresholds after looking at the first run's numbers rather than before, which guarantees they will always pass.

From the field

A Wellington-based fintech was preparing for open banking go-live under the Commerce Commission's guidelines and assumed their API could handle regulated traffic volumes because it "felt fast" in UAT. We ran a k6 soak test — 300 virtual users sustained for 45 minutes — and watched p95 climb from 180 ms at the five-minute mark to 1.4 seconds at minute thirty. The culprit was a connection pool that exhausted silently; functional tests never touched it because they never ran long enough. The team added an explicit maxConnections setting, re-ran the soak, and p95 stayed flat. The lesson that generalises: short load tests hide resource leaks that only surface under time pressure, not just user pressure.

4 Watch Me Do It — NZ Government API Spike Test

Scenario: Simulating a sudden surge of traffic to a public rates-payment API.

import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  // Define the load "shape"
  stages: [
    { duration: '30s', target: 100 }, // Ramp up to 100 users
    { duration: '1m', target: 100 },  // Stay at 100
    { duration: '20s', target: 0 },   // Ramp down
  ],
  // Define performance "gates"
  thresholds: {
    http_req_duration: ['p(95)<500'], // 95% of requests must be < 500ms
    http_req_failed: ['rate<0.01'],   // Errors must be < 1%
  },
};

export default function () {
  const res = http.get('https://api.govt.nz/rates/search?postcode=6011');
  
  // Functional check during load
  check(res, {
    'is status 200': (r) => r.status === 200,
    'has results': (r) => r.json().results.length > 0,
  });

  sleep(1); // Wait 1s between "user" actions
}

5 Decision Tool — Why k6?

✅ Choose k6 for...

  • CI/CD automated performance gates
  • API load, stress, and spike testing
  • Teams comfortable with JavaScript
  • Lightweight, single-binary execution

❌ Choose Others for...

  • JMeter: If you need a GUI and complex XML logic
  • Lighthouse: For frontend/rendering performance
  • Gatling: If your team is purely Scala-based

6 Common Mistakes

🚫 Assuming 200 OK = Success

Mistake: Not using check() to verify the response body.
Why: During a load test, a server might return a 200 OK but send an empty body or an error message inside the JSON. Always check for expected content.

🚫 Testing Against Production

Mistake: Running a 10,000 user load test against your live site.
Why: This is essentially a self-inflicted DDoS attack. Always use a staging environment that matches production specs as closely as possible.

7 Now You Try — Setup

🚀 Getting Started

Install k6 on your machine (Windows/Chocolatey):

choco install k6

Run a simple test against the k6 demo site:

k6 run https://test.k6.io

Why teams fail here

  • No warm-up stage: Ramping straight to peak load skips the JIT compilation and connection warm-up that production traffic always gets, producing artificially bad numbers that teams then dismiss as "just k6 being weird."
  • Testing the wrong layer: Running k6 against a staging environment backed by a single-container database with no read replicas, then wondering why production with a properly scaled RDS cluster behaves completely differently under load.
  • Ignoring the http_req_failed metric: Focusing only on response time while a 3 % error rate quietly hides in the results — in an NZ banking context, that 3 % is real customer transactions failing, not noise.
  • Running load tests on an ad-hoc basis: Treating k6 as a one-off pre-launch activity rather than a CI gate means performance regressions get merged undetected for weeks before anyone notices the slowdown in production metrics.

Key takeaway

A load test without a threshold is just a graph — it is the threshold that turns observation into a deployable standard.

8 Self-Check

Q1. What is a "Spike Test"?

A test that simulates a sudden, extreme surge in traffic (e.g., from 0 to 5,000 users in 10 seconds) to see if the system crashes or scales gracefully.

Q2. Does k6 measure how long a page takes to render in the browser?

No. k6 tests at the protocol level (HTTP). It measures how long the server takes to send the data, but not how long the browser takes to display it. Use Lighthouse for rendering performance.

9 Interview Prep

"How do you integrate performance testing into a CI/CD pipeline?"

Answer: "I use k6 to define Thresholds (SLOs) directly in the test script. For example, I'll set a threshold that the p95 response time must be under 500ms. I then run k6 in a GitHub Action; if the threshold is breached, k6 returns a non-zero exit code, which fails the build and prevents the degraded code from being deployed."