PABCD Initiative Documentation Hub pabcd_initiative codexclaw cli-jaw

Load & Performance Testing

Source: dev-testing/references/load-testing.md

Load & Performance Testing

> Deep reference for dev-testing §1.3 risk-first priorities and CI pipeline performance gates.

Last reviewed: 2026-06-16

Applies to: k6 0.52.x, Locust 2.x, Node 22+, Python 3.12+

When to read: task_tags includes performance, load_test, or C3+ production-facing API work

Canonical owner: dev-testing — infrastructure-level performance (auto-scaling, resource sizing) → dev-devops

---

§1 When to Load Test

Load testing is required when any of these apply:

  • [ ] C3+ classification with production traffic
  • [ ] Public API with external consumers
  • [ ] Expected traffic spike (launch, campaign, seasonal)
  • [ ] Infrastructure migration (DB, CDN, region)
  • [ ] Performance SLO defined in product requirements

Skip for C0–C2 internal tools with <10 concurrent users. Use unit benchmarks (vitest bench, pytest-benchmark) for micro-optimization instead.

---

§2 Tool Selection

Criterionk6 0.52 ✅Locust 2.x ✅Artillery 2.x
LanguageJavaScript/TypeScriptPythonYAML + JS hooks
ProtocolHTTP, gRPC, WebSocket, browserHTTP, customHTTP, WebSocket, Socket.IO
Distributedk6 cloud / k6 operator (K8s)built-in master/workerArtillery Cloud
CI integrationgrafana/setup-k6-action@v1pip install locust + scriptnpx artillery
Browser perfk6/browser (Chromium)✕✕
DashboardGrafana Cloud k6 / InfluxDBbuilt-in web UIArtillery Cloud
Best forJS/TS teams, CI gates, CWVPython teams, custom protocolsYAML-first, quick smoke

Decision rule: JS/TS stack → k6. Python stack or custom protocol → Locust. Neither is wrong; pick one and standardize.

Banned: Running both k6 and Locust in the same project without a documented reason.

---

§3 Test Types

TypePurposeWhenVUsDuration
SmokeBasic function under minimal loadEvery PR1–530s
LoadNormal traffic verificationmain merge / nightly50–2005–10 min
StressFind breaking pointPre-releaseRamp to limit15–30 min
SpikeSudden burst resiliencePre-release0 → max → 05–10 min
SoakLong-term stability (memory leaks, connection pool)Weekly / pre-releaseNormal load1–4 hr

Start with smoke in CI. Add load/stress only after smoke is stable.

---

§4 k6 Quick Start

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

export const options = {
  scenarios: {
    smoke: {
      executor: "shared-iterations",
      vus: 5,
      iterations: 50,
    },
  },
  thresholds: {
    http_req_duration: ["p(95)<300", "p(99)<500"],
    http_req_failed: ["rate<0.01"],
    checks: ["rate==1.0"],
  },
};

export default function () {
  const res = http.get(`${__ENV.BASE_URL}/api/health`);
  check(res, {
    "status 200": (r) => r.status === 200,
    "body has status": (r) => JSON.parse(r.body).status === "ok",
  });
  sleep(1);
}
  • thresholds are the pass/fail gate — without them, the test measures but never judges
  • checks validate response correctness; thresholds on checks enforce the gate
  • Run locally: k6 run --env BASE_URL=http://localhost:3000 tests/perf/smoke.js
  • Run with cloud dashboard: k6 cloud tests/perf/smoke.js (requires K6_CLOUD_TOKEN)

---

§5 Measure → Profile → Verify

1. Measure    ──→  k6 baseline (smoke → load → record p95/p99/error rate)
       │
2. Profile    ──→  identify bottleneck (traces, flame graphs, DB slow query log)
       │
3. Optimize   ──→  hypothesis-driven change (N+1 fix, cache, query tune)
       │
4. Re-measure ──→  same scenario, compare against baseline
       │
5. Gate       ──→  CI threshold pass → merge; fail → block deploy
BannedSymptomFix
"It's slow, add cache" without profilingCache added but wrong bottleneckMeasure first — k6 run → flame graph → then decide
Screenshot-only resultsNo regression detection possibleExport JSON/HTML report: k6 run --out json=results.json
One-time manual run, no CIPerformance regresses silentlyAdd smoke to PR pipeline (§6)

---

§6 CI Integration

# .github/workflows/performance.yml
name: Performance
on:
  pull_request:
    branches: [main]
  push:
    branches: [main]

jobs:
  smoke:
    if: github.event_name == 'pull_request'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: grafana/setup-k6-action@v1
      - uses: grafana/run-k6-action@v1
        with:
          path: tests/perf/smoke.js
          flags: --env BASE_URL=http://localhost:3000

  load:
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: grafana/setup-k6-action@v1
      - uses: grafana/run-k6-action@v1
        env:
          K6_CLOUD_TOKEN: ${{ secrets.K6_CLOUD_TOKEN }}
        with:
          path: tests/perf/load.js
          cloud-run-locally: true
  • PR: smoke only (fast, <1 min)
  • main merge: full load test with cloud dashboard
  • Release branch: stress + soak (manual trigger or scheduled)
  • Threshold failure = non-zero exit = blocked deploy

Banned: Load testing directly against production. Use staging or a dedicated perf environment.

---

§7 Anti-Patterns

BannedSymptomFix
Threshold-free load test"We ran k6" but no pass/fail judgmentAdd thresholds with p95/p99/error rate targets
Production load testDegraded user experience, potential outageUse staging or isolated perf environment
Tool salad (k6 + Locust + Artillery in one project)Inconsistent results, maintenance burdenPick one (§2 decision table), standardize
Hardcoded URLs in test scriptsBreaks across environmentsUse __ENV.BASE_URL or equivalent env vars
Ignoring soak testsMemory leaks and connection pool exhaustion shipSchedule weekly soak on staging

Pre-flight

Before merging a load test change:

  • [ ] thresholds defined with p95, p99, and error rate targets
  • [ ] Test runs against staging or local — never production
  • [ ] BASE_URL parameterized via environment variable
  • [ ] CI workflow updated (smoke on PR, load on main)
  • [ ] Results exported as JSON/HTML, not screenshots
  • [ ] Measure → Profile → Verify loop documented for any optimization claim
  • [ ] Cross-ref: dev-devops for infra-level scaling; ci-pipeline.md for pipeline placement

---

Cross-References

  • dev-testing §5 CI Pipeline — pipeline stage ordering
  • references/ci-pipeline.md — full GHA/GitLab CI templates
  • dev-backend §9 — p95 SLO definitions and observability
  • dev-devops — infrastructure-level performance (auto-scaling, resource allocation)