- Skill ID
- petrkindlmann/qa-skills/performance-testing
- Publisher
- petrkindlmann
- Repository
- qa-skills
- Installs
- 0
- Files
- 2
- License
- MIT
- Synced
- Sep 23, 2026
Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.
petrkindlmann/qa-skills/performance-testingInstalls these files- SKILL.md
- references/recipes.md
What this skill tells the agent
<objective> Measure, assert, and protect application performance with budgets enforced in CI, not subjective "feels fast enough" assessments. This skill covers two domains: load testing (can the backend handle traffic?) and web performance (is the frontend fast for users?). A report that says "LCP is 3.2s" is information; a CI gate that fails the build at 2.5s is accountability. </objective>
Quick Route
| You need to... | Go to |
|---|---|
| Test backend capacity / throughput / latency under traffic | k6 Load Testing → references/recipes.md |
| Pick a load shape (constant / ramp / spike / soak) | Load Profiles table |
| Measure frontend speed for real users (LCP, INP, CLS) | Web Performance + Core Web Vitals |
| Gate page perf in CI | Lighthouse CI (gate TBT, not INP — see below) |
| A budget is breached and you must find why | Bottleneck Identification |
| Migrate an existing suite from k6 v1 → v2 | k6 v2 Migration callout |
Discovery Questions
Check .agents/qa-project-context.md first — if it exists, use it and skip questions already answered there.
What to Measure
- Web performance or load testing? Web performance measures user-perceived speed (Core Web Vitals, page load). Load testing measures server capacity (RPS, latency under load). Most products need both.
- Which user journeys are performance-critical? Not every endpoint needs load testing. Focus on high-traffic, revenue-critical, or latency-sensitive flows.
- Existing performance budgets? If yes, what are the targets? If no, this skill helps establish them — measure baseline first.
Current State
- What is the current baseline? You need numbers before you can set targets. Measure first, then define budgets.
- What broke due to performance before? Slow pages that lost users, endpoints that timed out under load, queries that locked up — these point at where to focus.
- Existing monitoring? APM (Datadog, New Relic), RUM, or synthetic probes give you field data to model realistic load.
Infrastructure
- Where does load testing run? Target staging or a dedicated load-test environment, never production without explicit operations coordination.
- Expected traffic pattern? Steady, daily peaks, seasonal spikes (Black Friday), or event-driven bursts — this picks the load profile.
- Rate limits, WAF rules, or auto-scaling in the path? These distort results and must be accounted for in test design.
Core Principles
1. Measure before optimizing
Performance intuition is unreliable; developers routinely optimize the wrong thing. Profile first, identify the actual bottleneck, then optimize. A profiled 50ms win in the right place beats an assumed 500ms win in the wrong one.
2. Budgets are only real if CI enforces them
A budget documented in a wiki but not checked in CI is violated within weeks. Wire budgets into the pipeline as k6 thresholds and Lighthouse assertions so regressions fail the build, not a quarterly review.
3. Realistic load beats maximum stress
A stress test to 10x traffic tells you the breaking point. A load test at 1.5x expected traffic tells you whether tomorrow's real users have a good experience. Both have value, but realistic load runs more often and catches regressions earlier.
4. Core Web Vitals are what users actually feel
Server-side metrics (latency, throughput) matter, but users experience performance through the browser. LCP, INP, and CLS measure perceived speed. A fast API that renders slowly is still slow to users.
5. Performance is a feature
It does not happen by accident. It needs dedicated test infrastructure, budgets, and monitoring, and the same continuous attention as functional correctness. Treat a performance regression with the same urgency as a functional bug.
k6 Load Testing
k6 is an open-source load testing tool that uses JavaScript/TypeScript for scripts, runs from the CLI, and integrates with CI. Current stable: k6 v2.0.0 (final shipped 2026-05). v2 has breaking changes from v1 — see the migration callout below.
A load test is built from three pieces: a load profile (the stages/scenarios shape), checks (per-request assertions), and thresholds (pass/fail budgets that set the exit code). Always drive the base URL from __ENV so the same script runs against local, staging, and CI.
See references/recipes.md for the full basic load test, custom metrics, and scenarios. A minimal threshold block:
export const options = {
stages: [
{ duration: '1m', target: 20 },
{ duration: '3m', target: 20 },
{ duration: '1m', target: 0 },
],
thresholds: {
http_req_duration: ['p(95)<500', 'p(99)<1000'],
http_req_failed: ['rate<0.01'],
},
};Load Profiles
| Profile | Question | Shape | Duration |
|---|---|---|---|
| Constant | Can the system handle normal traffic? | vus: 50, duration: '10m' | 10 min |
| Ramp-up (stress) | At what point does it degrade? | 50 → 100 → 200 → 400 → 800 → 0 | 12 min |
| Spike | Does it recover from a sudden surge? | 50 → spike 500 → sustain → drop 50 → recover | 6 min |
| Soak | Does it leak resources over time? | Ramp to 100, sustain 4h, ramp down | 4+ hours |
A spike test is not done until "does it recover?" is an assertion, not a comment. Tag the post-spike window (e.g. phase:recovery) and scope a threshold to it so the run fails if p95 stays elevated. See the recovery-detection recipe in references/recipes.md.
Custom Metrics and Scenarios
k6 has four metric types: Counter (cumulative count), Rate (proportion of non-zero/true values, 0..1), Trend (statistical distribution — p50/p95/p99), Gauge (latest value). Tag requests with { tags: { name: 'endpoint' } } to filter metrics per endpoint, scenario, or flow.
Scenarios run distinct user flows concurrently, each with its own executor and per-scenario thresholds ('http_req_duration{scenario:checkout}': ['p(95)<500']). Use them to model a real mix — browsers + checkout + API-heavy load at once. Full custom-metric and scenario examples are in references/recipes.md.
k6 CI Integration
Install k6 with the official `grafana/setup-k6-action@v1` — not a hand-rolled apt/gpg keyserver block (brittle, rots, no version pin). k6 exits non-zero when any threshold is breached, so a breached budget fails the job with no extra wiring. The full GitHub Actions workflow (checkout → setup-k6 → run → upload artifact) is in references/recipes.md.
k6 v1 → v2 migration (v2.0.0 final, 2026-05): -k6/experimental/websockets→k6/websockets(drop theexperimental/prefix; stable now) -k6/experimental/redis→ `k6/x/redis` — NOT removed. The import auto-resolves thexk6-redisextension (auto-extension-resolution is on by default; JS usage unchanged). Do not hand-roll a Redis client. -externally-controlledexecutor removed -options.ext.loadimpactremoved → useoptions.cloud(Grafana Cloud k6, formerly k6 Cloud / Load Impact) - CLI:--no-summary→ `--summary-mode=disabled`;--upload-only→k6 cloud upload script.js;k6 login/pause/resume/scale/statusremoved (usek6 cloud login, etc.); positionalk6 cloud script.jsremoved - Exit code 97 is new: a non-threshold cloud-side abort. Wire it into CI handling. Reference: https://grafana.com/docs/k6/latest/get-started/migrating-to-v2/
Web Performance
Lighthouse CI
Lighthouse CI (@lhci/cli) automates Google Lighthouse audits and enforces budgets in the pipeline via lighthouserc.js assertions. Current: @lhci/cli 0.15.x on the Lighthouse 12.6 engine. The project is in maintenance mode (last release ~a year ago) and does not yet support Lighthouse 13 (needs Node 22.19+); it remains the standard CI surface for Lighthouse, but watch upstream before adopting in greenfield projects.
More from petrkindlmann
Use this skill in a project
Open any RiverX project, search the Skills panel for petrkindlmann/qa-skills/performance-testing, and install it in one click.
