---
title: "Virtual Users, Think Times, and AI Suggestions: Sanity Checks Before You Run"
description: "Sanity-check VUs and think time when AI suggests k6 scenarios: open vs closed arrival, sleep distributions, and why zero-delay scripts mislead everyone."
publishDate: "2026-09-22"
draft: false
keyword: "ai load test scenarios"
intent: "MOFU"
cta: "Use Performate's desktop workflow—imports, k6 runs, and AI-assisted analysis where your plan allows—to ship faster without skipping validation."
tags: ["k6","load-testing","ai","performate","load","test"]
---

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](https://k6.io/docs/using-k6/scenarios/executors/)). 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](/en/blog/stress-vs-load-vs-spike-testing)). 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.

```javascript
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](/en/blog/roi-ai-assisted-k6-workflows).
- **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.

```bash
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 says | Use executor | Sanity check |
|:---|:---|:---|
| "X req/s" or "orders/minute" | `constant-arrival-rate` | Achieved RPS ≈ target |
| "X concurrent users" | `constant-vus` (often) | Implied RPS vs analytics |
| Flash traffic step | `ramping-arrival-rate` spike | Not zero think on writes |
| CI smoke | `per-vu-iterations` | Separate 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](/en/blog/when-not-to-use-ai-load-testing) 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](https://performate.app) | [Book a demo](/demo) | [ROI of AI workflows](/en/blog/roi-ai-assisted-k6-workflows)
