Checklist item PF-01 says: critical flows (a path a user takes that costs money or trust if it breaks: sign-up, checkout, invite, export) within agreed budgets under baseline load. The pass criterion is p95 latency within the written budget per flow and an error rate under 1% of requests in the run. The tool is k6. This post is what those words mean in practice.
Baseline load
Not a load test. A smoke is the traffic you already have, replayed against staging for a few minutes: if your busiest hour is 40 concurrent users, the smoke runs 40 virtual users for five minutes across the critical flows. Its job is to catch a release that made something slow, not to find the ceiling.
A budget
A number per flow, written down before the first run. For a typical SaaS: sign-in under 800 ms at p95, dashboard load under 1.5 s, list endpoints under 500 ms, checkout under 2 s end to end. The numbers come from what the product does today plus what you would accept; the point is that they are written, so a release that goes from 400 ms to 1.4 s on a 500 ms budget fails on a number.
The script
import http from "k6/http";
import { check } from "k6";
export const options = {
vus: 40,
duration: "5m",
thresholds: {
"http_req_duration{flow:list}": ["p(95)<500"],
"http_req_duration{flow:dashboard}": ["p(95)<1500"],
http_req_failed: ["rate<0.01"],
},
};
export default function () {
const list = http.get(`${__ENV.BASE}/api/invoices?page=1`, { tags: { flow: "list" } });
check(list, { "list 200": (r) => r.status === 200 });
const dash = http.get(`${__ENV.BASE}/dashboard`, { tags: { flow: "dashboard" } });
check(dash, { "dashboard 200": (r) => r.status === 200 });
}
Thresholds are the budget. k6 exits non-zero when one is missed, which is what makes it a checklist item rather than a report.
What it usually finds
The pattern behind most smoke failures is one query per row: a list page that runs a separate query for each item, fine with 20 rows in development and slow with 2,000 in staging. PF-03 counts queries per request for exactly this. The second most common is an unbounded list endpoint (PF-02) that returns everything because nobody wrote a page size.
Where it runs
In your CI (the pipeline that builds and tests every change before it can be merged or deployed), against staging with a realistic dataset, on every release candidate for Growth and Scale. Starter does not include performance items; the limits table says so.