load-testing-plan

$npx mdskill add mohitagw15856/pm-claude-skills/load-testing-plan

Produce a complete load and performance testing plan for a service — covering test objectives, scenario definitions, tooling configuration, success thresholds, and CI integration. A good load testing plan eliminates ambiguity about what "performance is acceptable" means, so engineers can run tests and get a pass/fail answer without having to interpret raw numbers themselves.

SKILL.md

.github/skills/load-testing-planView on GitHub ↗
---
name: load-testing-plan
description: "Write a load and performance testing plan for a service. Use when asked to create a performance test plan, write load testing documentation, define stress or soak test scenarios, or set performance regression gates for CI. Produces a complete test plan document with scenario definitions, k6/Locust script skeleton, threshold table, result interpretation guide, and CI integration steps."
---

# Load Testing Plan Skill

Produce a complete load and performance testing plan for a service — covering test objectives, scenario definitions, tooling configuration, success thresholds, and CI integration. A good load testing plan eliminates ambiguity about what "performance is acceptable" means, so engineers can run tests and get a pass/fail answer without having to interpret raw numbers themselves.

## Required Inputs

Ask for these if not already provided:
- **Service name and key endpoints** — which endpoints are under test (path, method, typical request/response shape)
- **Current traffic baseline** — current requests/sec, p50/p99 latency, error rate under normal load
- **Peak traffic expectations** — expected peak RPS (e.g. 10× baseline for flash sales, or seasonality peak)
- **SLO targets** — latency SLOs (p99 < X ms), error rate SLO (< Y%), availability target
- **Preferred testing tool** — k6, Locust, JMeter, Gatling, or no preference
- **Test environment availability** — dedicated load test environment, staging, or production (with traffic shaping)

## Output Format

---

# Load Testing Plan: [Service Name]

**Author:** [Name] | **Team:** [Team name]
**Date:** [Date] | **Review cycle:** Before each major release and quarterly
**Testing tool:** [k6 / Locust / JMeter / Gatling]
**Test environment:** [Environment name and URL]

---

## 1. Objectives and Scope

**What we are testing:** [Service name] handles [describe function — e.g. "user authentication requests from the mobile and web clients"]. This plan validates that the service meets its SLOs under expected and elevated traffic conditions.

**In scope:**
- [Endpoint 1: METHOD /path — description]
- [Endpoint 2: METHOD /path — description]
- [Endpoint 3: METHOD /path — description]

**Out of scope:**
- [Any endpoints explicitly excluded and why — e.g. "admin APIs — low traffic, excluded from load test"]
- [Third-party integrations that cannot be load-tested — mock them instead]

---

## 2. Performance Targets (Success Criteria)

Every scenario has explicit pass/fail thresholds. A test run FAILS if any threshold is breached.

| Metric | Baseline scenario | Stress scenario | Spike scenario | Soak scenario |
|---|---|---|---|---|
| p50 latency | < [X] ms | < [X × 1.5] ms | < [X × 2] ms | < [X] ms |
| p95 latency | < [Y] ms | < [Y × 1.5] ms | < [Y × 2] ms | < [Y] ms |
| p99 latency | < [Z] ms | < [Z × 2] ms | < [Z × 3] ms | < [Z] ms |
| Error rate | < [0.1]% | < [1]% | < [2]% | < [0.1]% |
| Throughput | ≥ [N] RPS | ≥ [N × 3] RPS | N/A | ≥ [N] RPS |
| Failed requests | 0 (5xx) | < [threshold] | < [threshold] | 0 (5xx) |

**SLO reference:** These thresholds are derived from the service SLOs — p99 < [Z ms], error rate < [0.1]%, availability [99.9]%.

---

## 3. Traffic Model

**Baseline traffic (current production):**
- Average RPS: [N] req/sec
- Peak RPS (observed): [N] req/sec
- Request distribution by endpoint:
  - [Endpoint 1]: [X]% of traffic
  - [Endpoint 2]: [Y]% of traffic
  - [Endpoint 3]: [Z]% of traffic

**Simulated user behaviour:**
- Think time between requests: [X–Y] seconds (randomised)
- Session duration: [N] minutes average
- Authenticated vs anonymous ratio: [X]%/[Y]%
- Geographic distribution: [Region 1 X]%, [Region 2 Y]%

---

## 4. Test Scenarios

### Scenario 1: Baseline (Steady-State)

**Purpose:** Confirm the service performs acceptably under normal production load.
**Duration:** 10 minutes
**Load profile:** Ramp to [N] RPS over 2 minutes, hold for 8 minutes.
**Concurrency:** [N] virtual users

**Pass criteria:** All thresholds in the Baseline column of the targets table above.

---

### Scenario 2: Stress Test

**Purpose:** Find the breaking point — how much load can the service handle before SLOs are breached?
**Duration:** 20–30 minutes
**Load profile:** Ramp from [N] RPS (baseline) to [N × 5] RPS in 5-minute steps. Hold each step for 5 minutes. Stop at first SLO breach.
**Concurrency:** Scales with RPS target

**What to record:**
- RPS at which p99 latency first exceeds SLO
- RPS at which error rate first exceeds SLO
- Whether the service recovers when load drops back to baseline

---

### Scenario 3: Spike Test

**Purpose:** Simulate a sudden traffic surge (flash sale, viral event, bot attack).
**Duration:** 15 minutes
**Load profile:** Hold at [N] RPS (baseline) for 3 minutes, spike to [N × 10] RPS instantly, hold for 5 minutes, drop back to baseline for 7 minutes.

**What to record:**
- Latency during spike and recovery
- Whether the service sheds load gracefully (rate limiting, queue depth)
- Time to recover to baseline latency after spike ends

---

### Scenario 4: Soak / Endurance Test

**Purpose:** Detect memory leaks, connection pool exhaustion, and slow degradation over time.
**Duration:** 4–8 hours (run overnight)
**Load profile:** Steady [N × 1.5] RPS (50% above baseline) for entire duration.

**What to watch:**
- Memory usage trend over time (should not grow unboundedly)
- Error rate trend (should be flat, not creeping up)
- GC pause frequency (JVM/Go services)
- Database connection pool utilisation
- p99 latency trend (should not creep up over hours)

---

## 5. Test Environment Requirements

### Infrastructure

| Component | Requirement | Notes |
|---|---|---|
| Service under test | Isolated from production | [N] replicas, matching prod resource limits |
| Database | Separate instance with production-scale data | Seed script in section 7 |
| Cache (Redis/Memcached) | Empty at test start | Ensures cold-start conditions are tested |
| Load generator | Separate from service under test | [N] vCPUs, [N] GB RAM minimum |
| Network | Low-latency path to service | Do not run generator on same host |

### Data Seeding

Before every test run, ensure the environment has:
```bash
# Seed test users (needed for authenticated endpoint tests)
[seed command or script path — e.g. python scripts/seed_load_test_users.py --count 10000]

# Seed test data for read endpoints
[seed command — e.g. ./scripts/seed_products.sh --count 50000]

# Verify seed completed
[verification command — e.g. psql $DB_URL -c "SELECT COUNT(*) FROM users WHERE load_test=true"]
```

**Test data rules:**
- Never use real production user data in load tests
- Tag all test-generated records with `load_test=true` for easy cleanup
- Run cleanup after each test: `[cleanup command]`

---

## 6. Tooling Setup

### k6 Script Skeleton

```javascript
import http from 'k6/http';
import { check, sleep } from 'k6';
import { Rate, Trend } from 'k6/metrics';

// Custom metrics
const errorRate = new Rate('error_rate');
const endpointLatency = new Trend('endpoint_latency', true);

// Test configuration — override per scenario
export const options = {
  scenarios: {
    baseline: {
      executor: 'ramping-vus',
      startVUs: 0,
      stages: [
        { duration: '2m', target: [BASELINE_VUS] },
        { duration: '8m', target: [BASELINE_VUS] },
        { duration: '1m', target: 0 },
      ],
    },
  },
  thresholds: {
    http_req_duration: [
      'p(95)<[Y_MS]',
      'p(99)<[Z_MS]',
    ],
    error_rate: ['rate<0.01'],
    http_req_failed: ['rate<0.01'],
  },
};

// Auth helper — get token once per VU
export function setup() {
  const loginRes = http.post('[BASE_URL]/auth/login', JSON.stringify({
    username: `load_test_user_${Math.floor(Math.random() * 10000)}@example.com`,
    password: '[LOAD_TEST_PASSWORD]',
  }), { headers: { 'Content-Type': 'application/json' } });

  check(loginRes, { 'login ok': (r) => r.status === 200 });
  return { token: loginRes.json('access_token') };
}

export default function (data) {
  const headers = {
    Authorization: `Bearer ${data.token}`,
    'Content-Type': 'application/json',
  };

  // Endpoint 1: [Description]
  const res1 = http.get('[BASE_URL]/[endpoint-1]', { headers });
  check(res1, {
    '[endpoint-1] status 200': (r) => r.status === 200,
    '[endpoint-1] latency < [X]ms': (r) => r.timings.duration < [X],
  });
  errorRate.add(res1.status >= 400);
  endpointLatency.add(res1.timings.duration, { endpoint: '[endpoint-1]' });

  sleep(Math.random() * [THINK_TIME_MAX] + [THINK_TIME_MIN]);

  // Endpoint 2: [Description]
  const res2 = http.post('[BASE_URL]/[endpoint-2]',
    JSON.stringify({ [key]: '[value]' }),
    { headers }
  );
  check(res2, {
    '[endpoint-2] status 201': (r) => r.status === 201,
  });
  errorRate.add(res2.status >= 400);
}
```

### Locust Script Skeleton (alternative)

```python
from locust import HttpUser, task, between
import random

class [ServiceName]User(HttpUser):
    wait_time = between([THINK_TIME_MIN], [THINK_TIME_MAX])
    token = None

    def on_start(self):
        """Called once per simulated user — authenticate."""
        user_id = random.randint(1, 10000)
        response = self.client.post("/auth/login", json={
            "username": f"load_test_user_{user_id}@example.com",
            "password": "[LOAD_TEST_PASSWORD]",
        })
        self.token = response.json()["access_token"]
        self.headers = {"Authorization": f"Bearer {self.token}"}

    @task([WEIGHT_1])  # Weight = relative frequency
    def [endpoint_1_task](self):
        """[Endpoint 1 description]"""
        with self.client.get(
            "/[endpoint-1]",
            headers=self.headers,
            catch_response=True
        ) as response:
            if response.elapsed.total_seconds() > [LATENCY_THRESHOLD]:
                response.failure(f"Too slow: {response.elapsed.total_seconds()}s")

    @task([WEIGHT_2])
    def [endpoint_2_task](self):
        """[Endpoint 2 description]"""
        self.client.post(
            "/[endpoint-2]",
            json={"[key]": "[value]"},
            headers=self.headers,
        )
```

### Running Tests

```bash
# k6 — run baseline scenario
k6 run --env BASE_URL=https://[test-env-url] scripts/load_test.js

# k6 — run stress scenario with output to InfluxDB
k6 run --out influxdb=http://[influxdb-host]:8086/k6 \
  --env SCENARIO=stress \
  scripts/load_test.js

# Locust — headless run
locust -f locustfile.py \
  --headless \
  --users [N] \
  --spawn-rate [N] \
  --run-time 10m \
  --host https://[test-env-url] \
  --csv=results/[run-id]

# Locust — web UI (interactive)
locust -f locustfile.py --host https://[test-env-url]
```

---

## 7. Metrics to Capture

Capture all of the following during every test run. Missing any of these makes result comparison unreliable.

| Metric | Source | Why it matters |
|---|---|---|
| p50, p95, p99, p999 latency per endpoint | Load tool | SLO validation |
| Error rate (4xx, 5xx) per endpoint | Load tool | SLO validation |
| Requests/sec (throughput) | Load tool | Capacity baseline |
| CPU utilisation (%) | Infra monitoring | Saturation signal |
| Memory utilisation (%) | Infra monitoring | Leak detection |
| GC pause time / frequency | JVM/Go metrics | Latency spike root cause |
| DB connection pool: active/idle/waiting | DB metrics | Pool exhaustion detection |
| DB query latency (p99) | DB metrics | Downstream bottleneck |
| Cache hit rate | Cache metrics | Miss storm detection |
| Pod/instance count (if autoscaling) | Infra | Scaling behaviour |
| Network in/out bytes | Infra | Bandwidth saturation |

---

## 8. Result Analysis Framework

After each test run, work through this analysis in order:

**Step 1 — Pass/fail check**
Compare all captured metrics against the thresholds in Section 2. Record pass/fail per scenario.

**Step 2 — Latency distribution**
Plot the full latency histogram, not just percentiles. A bimodal distribution (two humps) indicates two distinct code paths — investigate the slow hump.

**Step 3 — Error correlation**
If errors occurred, correlate them with:
- Time of occurrence (was it during ramp-up, steady state, or spike?)
- Specific endpoint (is it one endpoint or all?)
- Infrastructure events (CPU spike, OOM, DB connection exhaustion?)

**Step 4 — Saturation analysis**
Graph CPU, memory, and connection pool over time. If any resource reached 80%+ of capacity, it is a candidate bottleneck — even if SLOs passed this run.

**Step 5 — Compare to baseline run**
Every run should be compared to the previous run. A 10% regression in p99 latency warrants investigation even if it is still within SLO.

**Regression classification:**

| Change | Classification | Action |
|---|---|---|
| p99 within 5% of previous run | Green — no regression | No action |
| p99 5–15% worse than previous | Yellow — watch | Investigate before next release |
| p99 >15% worse than previous | Red — regression | Block release, file ticket |
| Error rate increased vs previous | Red — regression | Block release |
| SLO threshold breached | Critical | Block release, page on-call |

---

## 9. CI Integration

Add load tests as a gated step in the release pipeline. Run the baseline scenario on every release candidate; run all scenarios weekly.

```yaml
# Example: GitHub Actions step (adapt for your CI platform)
load-test:
  runs-on: ubuntu-latest
  needs: [deploy-staging]
  if: github.ref == 'refs/heads/main'
  steps:
    - uses: actions/checkout@v3

    - name: Install k6
      run: |
        curl -s https://dl.k6.io/key.gpg | sudo apt-key add -
        echo "deb https://dl.k6.io/deb stable main" | sudo tee /etc/apt/sources.list.d/k6.list
        sudo apt-get update && sudo apt-get install k6

    - name: Seed test data
      run: [seed command]

    - name: Run baseline load test
      run: |
        k6 run \
          --env BASE_URL=${{ secrets.LOAD_TEST_ENV_URL }} \
          --out json=results.json \
          scripts/load_test.js
      env:
        LOAD_TEST_ENV_URL: ${{ secrets.LOAD_TEST_ENV_URL }}

    - name: Check thresholds
      run: |
        # k6 exits with non-zero if any threshold fails — this step fails the build
        echo "k6 threshold check complete"

    - name: Upload results
      uses: actions/upload-artifact@v3
      if: always()
      with:
        name: load-test-results-${{ github.run_id }}
        path: results.json

    - name: Cleanup test data
      if: always()
      run: [cleanup command]
```

**CI gates summary:**
- Baseline scenario runs on every release to staging
- Full scenario suite (stress, spike, soak) runs weekly on a schedule
- Any threshold failure blocks promotion to production
- Results are archived for trend analysis

---

## Quality Checks

- [ ] All key endpoints are covered by at least one test scenario — no production endpoint is untested
- [ ] Thresholds are derived from actual SLO targets, not guesses
- [ ] Test data seeding is scripted and reproducible — tests do not rely on pre-existing environment state
- [ ] The load generator runs on separate infrastructure from the service under test
- [ ] CI integration blocks promotion on threshold failure — not just records results
- [ ] Soak test has been run at least once to establish a memory and connection pool baseline
- [ ] Results comparison to previous run is part of the analysis — not just absolute pass/fail

More from mohitagw15856/pm-claude-skills

SkillDescription
360-feedback-templateDesign a 360-degree feedback survey or write a structured 360 feedback report. Use when asked to build a 360 feedback process, write 360 feedback for a colleague, design a feedback survey, or produce a feedback report. Produces either a complete survey instrument with rating scales and open-ended questions, or a structured narrative feedback report with themes, strengths, and development areas.
ab-test-plannerDesign statistically rigorous A/B tests for product features, UI changes, onboarding flows, and pricing experiments. Use when asked to set up an experiment, design an A/B test, calculate sample size, or interpret test results. Produces a complete test plan with hypothesis, variant definitions, sample size, duration estimate, guardrail metrics, and a results interpretation guide.
accessibility-auditGenerate a WCAG 2.2 accessibility audit checklist and remediation suggestions for any UI or design. Use when asked to audit for accessibility, check WCAG compliance, review a design for a11y issues, or create an accessibility remediation plan. Produces a prioritised checklist with pass/fail assessments and specific fixes.
account-planBuild a structured account plan for any key customer or target account. Use when asked to create an account plan, key account strategy, strategic account review, or territory plan. Produces a complete account plan with relationship map, growth opportunities, risks, and 90-day action plan.
aeo-optimizerOptimize an article for Answer Engine Optimization (AEO) — restructuring content so AI engines like ChatGPT, Perplexity, and Claude can extract, quote, and cite it. Rewrites headings as questions, drops 50-80 word answer capsules, audits paragraph length, and flags trust signals. Use when asked to AEO-optimize, make content AI-readable, improve AI citation chances, or adapt an article for answer engines.
ai-ethics-reviewConduct an ethical review of an AI or ML feature, model, or product. Use when asked to run an AI ethics review, assess AI risks, audit a model for bias, or produce an AI impact assessment. Produces a structured ethics review covering fairness, transparency, privacy, safety, accountability, and societal impact with prioritised mitigations.
ai-product-canvasStructure AI and ML product decisions with the rigour of any product decision. Use when building AI-powered features, evaluating LLM integrations, designing AI products, or assessing AI readiness. Produces a complete AI product canvas covering problem definition, model approach, data requirements, evaluation framework, UX design, responsible AI checklist, and launch monitoring plan.
ambiguity-resolverStructure vague opportunities and unclear briefs into actionable one-page problem statements. Use when asked to clarify a vague brief, frame an undefined problem, make sense of an unclear opportunity, or when the user says 'we need to figure out what to do about X' or 'I've been asked to look into Y'. Produces a structured problem brief with reframed questions, scoped boundaries, and a minimum viable research plan.
api-docs-writerWrite clear, developer-facing API documentation. Use when asked to document an API endpoint, write API reference docs, create a developer guide, or turn a raw spec/Postman collection into documentation. Produces endpoint documentation with descriptions, parameters, request/response examples, and error codes.
api-versioning-strategyWrite an API versioning strategy document for a service or API platform. Use when asked to define versioning policy, plan API deprecation, classify breaking changes, or document version lifecycle. Produces a complete versioning strategy with breaking-change classification table, deprecation timeline, migration guide template, and client communication template.