Performance engineering as a governed system: typed k6 workloads, controlled targets, SLO-based gates, reviewed regression baselines, Grafana/InfluxDB observability, OpenTelemetry, and six distinct workload models for the RealWorld API.
| Evidence | Direct link |
|---|---|
| Live reports | Published performance evidence |
| Current release | v0.4.0 |
| Primary CI | Main load workflow |
| Safety and quality | Quality · Security |
| Repository activity | Default-branch commits · Pull requests |
| Architecture and decisions | Architecture · ADR index |
| Failure evidence | Threshold failure example · Interpretation guide |
| Engineering question | Implemented answer |
|---|---|
| How is unsafe load prevented? | Public targets are read-only; sustained and write-heavy workloads require a controlled target and explicit override. |
| How are regressions distinguished from noise? | Reviewed three-run baselines, p95/p99 comparisons, tolerance rules, and compatible-run selection. |
| How is performance made diagnosable? | Tagged metrics, JSON and Markdown summaries, Grafana/InfluxDB dashboards, and OpenTelemetry export. |
| How does the suite model different risks? | Smoke, load, stress, spike, soak, and breakpoint workloads with separate intent and thresholds. |
| How does CI remain credible? | Quality, security, main-load, reports, scheduled-soak, and release gates publish durable evidence. |
Release v0.4.0 establishes the review-ready performance platform: controlled
load targets, advanced workload gates, regression tooling, report publication,
observability capture, security scanning, and documented operational limits.
See release notes and the changelog.
flowchart LR
T["k6 test entry points"] --> S["Reusable scenarios"]
S --> A["Typed API services"]
A --> H["Tagged HTTP client"]
H --> R["RealWorld API"]
T --> O["JSON and Markdown summaries"]
T --> I["InfluxDB v2"]
T --> C["OpenTelemetry Collector"]
I --> G["Grafana dashboard"]
O --> P["CI gates and GitHub Pages"]
Tests never call k6/http directly. Endpoint services own paths, payload envelopes, authentication,
and stable metric names. Write-heavy tests reject non-local targets unless explicitly overridden.
| Type | Purpose | Default target | Command |
|---|---|---|---|
| Smoke | Fast API and SLO validation | Hosted API, read-only | TARGET_ENV=staging npm run smoke |
| Load | Expected traffic and user journeys | Controlled local API | npm run load |
| Stress | Progressive degradation | Controlled local API | npm run stress |
| Spike | Sudden traffic and recovery | Controlled local API | npm run spike |
| Soak | Long-duration stability | Controlled local API | npm run soak |
| Breakpoint | Capacity boundary | Controlled local API | npm run breakpoint |
Use npm run breakpoint:search to run a binary-search capacity probe. Its JSON output reports
lastHealthyVus, firstUnhealthyVus, and toleranceVus.
| Endpoint group | p95 | p99 | Error rate |
|---|---|---|---|
| Authentication | < 800 ms | < 1500 ms | < 1% |
| Article reads | < 10 ms | < 20 ms | < 1% |
| Article writes | < 1000 ms | < 2000 ms | < 2% |
| Comment reads | < 5 ms | < 10 ms | < 2% |
| Profiles | < 5 ms | < 10 ms | < 1% |
| Tags | < 5 ms | < 10 ms | < 1% |
See performance SLOs for enforcement details.
The hosted read-only smoke test uses wider network-facing guardrails defined separately in
config/thresholds/smoke.ts.
Prerequisites: Node.js 20+, npm 10.9.4, k6 2.0+, Docker Desktop, and Docker Compose.
npx --yes npm@10.9.4 ci
npm run typecheck
npm run lint
npm run test:unit
npm run buildRun the read-only validation profile:
TARGET_ENV=staging TEST_PROFILE=validation SUMMARY_NAME=smoke npm run smoke$env:TARGET_ENV = 'staging'
$env:TEST_PROFILE = 'validation'
$env:SUMMARY_NAME = 'smoke'
npm run smokeStart observability and run a local observed load:
npm run docker:up
npm run docker:health
npm run load:observed
npm run observability:captureGrafana is available at http://localhost:3001 with local credentials admin / admin.
Grafana evidence is produced by local observed runs because GitHub-hosted CI does not persist a
remote InfluxDB. CI publishes JSON, Markdown, and Pages artifacts instead.
Authenticated scenarios create a disposable setup user against an authorized writable target:
export TARGET_ENV=local
export BASE_URL=http://localhost:3000/api
export TEST_PROFILE=validation
npm run load:journey$env:TARGET_ENV = 'local'
$env:BASE_URL = 'http://localhost:3000/api'
$env:TEST_PROFILE = 'validation'
npm run load:journeySet AUTH_USER_COUNT to size the setup user pool. VUs select credentials deterministically from
that pool, avoiding a single-account bottleneck. Full non-local workloads also require explicit
K6_STAGES; built-in full profiles are limited to the controlled local target.
- PR smoke posts an aggregate Markdown summary to same-repository pull requests.
- Main load, regression, and advanced workload workflows provision a pinned RealWorld backend.
- Load regression checks compare aggregate, endpoint, and business-metric p95/p99 values with a reviewed three-run baseline. Full advanced runs compare with their latest compatible retained run.
- Release publication waits for full stress, spike, soak, and breakpoint evidence.
- GitHub-hosted segmented validation proves partition wiring; the manual distributed workflow is reserved for authorized self-hosted runners and a shared controlled target.
- Security CI runs npm audit, creates a CycloneDX SBOM, and scans the lockfile with OSV.
- Published performance reports
- Architecture
- Contributor onboarding
- Adapting to another API
- Engineering notes
- Results interpretation
- Visual performance evidence
- Architecture decisions
- Capability status
- v0.4.0 release notes
- v0.3.0 release notes
- v0.2.0 release notes
Public endpoints are restricted to short read-only checks. Authenticated writes and sustained load
default to a local target and require ALLOW_NON_LOCAL_LOAD=true elsewhere. Tokens, generated
runtime reports, and unreviewed baseline captures are excluded from version control. The local
observability stack ships with development credentials and anonymous Grafana viewer access; keep
those ports local or rotate credentials before exposing the stack.