Skip to main content
Sep 22, 2026ai load test scenarios

By Lucas Yoris · Performate

Virtual Users, Think Times, and AI Suggestions: Sanity Checks Before You Run

Sanity-check VUs and think time when AI suggests k6 scenarios: open vs closed arrival, sleep distributions, and why zero-delay scripts mislead everyone.

AI loves sleep(0) and 500 VUs because it looks impressive in a script. Stakeholders then believe you tested "500 users" when you actually fired unrealistic RPS at a closed loop with no think time—false capacity signals, false failures, and release decisions built on fiction.

Before you run any AI-suggested k6 scenario, sanity-check executor model, VU count vs arrival rate, and sleep distribution against analytics (k6 arrival rate vs VUs). This guide explains open vs closed models, think time rules, a realistic browse→cart k6 example, and an AI review checklist your team can paste into every perf ticket.

Open vs closed models

The executor choice is a model of the world, not a syntax preference:

  • Closed (constant-vus, ramping-vus): fixed concurrent users; RPS emerges from response time. If the system slows, each user waits longer but stays active—RPS may drop OR queueing per user increases load in ways product did not describe.
  • Open (constant-arrival-rate, ramping-arrival-rate): fixed business RPS; VUs scale with latency. Matches "500 orders per minute" or "checkout API at 40 req/s."

Pick the model that matches how product and analytics describe load (stress vs load). If the ticket says "500 concurrent shoppers browsing," closed may fit. If it says "500 checkouts per minute," open is required.

Red flags in AI drafts

  • VUs > 10× target RPS without long think time—implies machine-gun requests.
  • sleep(0) on write paths—double-check always.
  • constant-vus when product quote was strictly RPS—executor mismatch.
  • Missing setup() for auth AI removed "for simplicity."

Think time rules of thumb

Think time shapes implied RPS more than VU count in closed models:

  • Use median inter-click time from logs or analytics—not zero because k6 felt slow.
  • Add jitter—sleep(2 + Math.random()) avoids synchronized waves that create artificial spikes.
  • Separate read vs write sleeps—browse longer than POST confirm.
  • Document assumed think time in report footer—stakeholders compare runs fairly.

Implied RPS (closed model, rough): VUs / (avg_response_time + think_time). Run one-minute pilot and compare implied RPS to analytics before scaling duration.

k6 sanity example

Example (illustrative). Browse→cart with ARR and jittered think time.

What this demonstrates:

  • constant-arrival-rate defines business load—product RPS statement maps directly.
  • preAllocatedVUs / maxVUs caps—k6 scales VUs without unbounded goroutines client-side.
  • Jittered sleep after browse—realistic pacing, not synchronized hammering.
  • Separate tags on browse vs cart for report segmentation.
import http from 'k6/http';
import { check, sleep } from 'k6';

const BASE = __ENV.API_BASE || 'https://staging.example.com';

export const options = {
  scenarios: {
    realistic: {
      executor: 'constant-arrival-rate',
      rate: Number(__ENV.TARGET_RPS || 20),
      timeUnit: '1s',
      duration: '5m',
      preAllocatedVUs: 10,
      maxVUs: 80,
      tags: { ai: 'reviewed' },
    },
  },
  thresholds: {
    'http_req_duration{route:cart}': ['p(95)<900'],
    http_req_failed: ['rate<0.01'],
  },
};

export default function () {
  http.get(`${BASE}/browse`, { tags: { route: 'browse' } });
  sleep(2 + Math.random()); // think time ~2–3s
  const res = http.post(`${BASE}/cart`, '{}', {
    headers: { 'Content-Type': 'application/json' },
    tags: { route: 'cart' },
  });
  check(res, { ok: (r) => r.status < 500 });
  sleep(1);
}

Patterns that work

  • One-minute pilot at target executor—compare implied RPS and error rate before 10m run.
  • Lock executor in UI after AI draft—Performate visual editor prevents accidental model switch.
  • Tag ai:reviewed only after human sanity pass—pairs with ROI tracking.
  • Ask product: "RPS or concurrent users?"—document answer in ticket.

Anti-patterns to avoid

  • Running raw AI output on shared staging at full VUs—validate pilot first.
  • Using closed VUs for ARR SLO proof—model mismatch invalidates threshold story.
  • Removing sleeps to "save CI time"—false signal saves nothing.
  • Trusting AI-proposed thresholds without SLO doc cross-check.

Pro tip (example command): pilot run with JSON summary for implied RPS check.

k6 run sanity-browse-cart.js --duration 1m --env TARGET_RPS=20 --summary-export=pilot.json

What this command demonstrates: one-minute pilot produces iteration count and duration stats—divide completed iterations by 60 for rough achieved RPS vs target ARR before committing to long run.

Decision table: product language vs executor

Product saysUse executorSanity check
"X req/s" or "orders/minute"constant-arrival-rateAchieved RPS ≈ target
"X concurrent users"constant-vus (often)Implied RPS vs analytics
Flash traffic stepramping-arrival-rate spikeNot zero think on writes
CI smokeper-vu-iterationsSeparate from load model

AI review checklist

Before scaling any AI-suggested scenario:

  • Does product speak in RPS or concurrent users? Executor matches.
  • Are VUs > RPS without long think time? Flag and fix.
  • Did AI remove auth/setup? Restore from Postman import.
  • Compare implied RPS to staging capacity and analytics band.
  • Think time documented in ticket and report footer.
  • Tag ai:reviewed only after human sign-off (governance where required).

How Performate supports sanity checks before scale

Below is a concrete workflow example for reviewing AI-drafted browse/cart flow—adapt to your collection.

Example: import → AI draft → human lock → pilot

  1. Import real Postman collection—browse and cart requests ordered. Problem solved: AI cannot invent routes when import is mandatory first step.
  2. Let AI draft scenario structure—lock executor to ARR manually in UI. Problem solved: prevents impressive-but-wrong constant-vus defaults.
  3. Set think time fields from analytics note—2–3s browse, 1s post-cart. Problem solved: visual editor faster than hunting sleep() lines AI scattered.
  4. Run 1m pilot; read achieved RPS in report. Problem solved: catch model errors before thirty-minute staging reservation.
  5. Compare implied load to analytics band—adjust rate or sleep. Problem solved: stakeholders trust numbers tied to product language.
  6. Apply tag ai:reviewed and run full 5m—archive export for CI promotion path. Problem solved: audit trail from draft to approved run.

That workflow maps directly to the cta in this post: ship faster without skipping validation on VUs, think time, and executors.

Closing takeaway

Never run raw AI VU numbers. Translate product language to executors, add think time with jitter, pilot one minute, then scale duration—tag ai:reviewed only after a human would stake their name on the config.

Open your last AI-drafted script, check executor vs product quote, and run a one-minute pilot before the next full staging window.

Try Performate free | Book a demo | ROI of AI workflows

Ready to optimize your API performance?

Use Performate's desktop workflow—imports, k6 runs, and AI-assisted analysis where your plan allows—to ship faster without skipping validation.

← Back to all posts