DEV Community

myougaTheAxo
myougaTheAxo

Posted on

Load Testing with Claude Code: k6, SLA Thresholds, and Bottleneck Detection [20260311_142034]

Load Testing with Claude Code: k6, SLA Thresholds, and Bottleneck Detection

A production traffic surge caused your service to go down last Friday. The post-mortem conclusion: no load testing in CI. Claude Code can generate comprehensive k6 load test suites directly from your CLAUDE.md configuration.

What Goes in CLAUDE.md

Define your load test strategy once so Claude Code can generate consistent scripts:

## Load Testing Strategy

### Test Profiles
- **Smoke test**: 1 VU, 1 minute — verify script correctness
- **Load test**: target QPS, 5–30 minutes — baseline performance
- **Stress test**: gradual increase until failure — find the limit
- **Spike test**: 10x sudden burst — resilience check

### SLA Thresholds
- P95 response time < 500ms
- P99 response time < 1000ms
- Error rate < 0.1%

### Metrics to Collect
- VU count at each stage
- P50 / P95 / P99 latency
- Throughput (req/s)
- Error rate
Enter fullscreen mode Exit fullscreen mode

Generated k6 Script

Claude Code produces a production-ready k6 script with custom metrics, staged ramp-up, and SLA-aligned thresholds:

import http from 'k6/http';
import { check, group, sleep } from 'k6';
import { Trend, Counter, Rate } from 'k6/metrics';
import { textSummary } from 'https://jslib.k6.io/k6-summary/0.0.2/index.js';

// Custom metrics
const orderCreationDuration = new Trend('order_creation_duration', true);
const errors = new Counter('errors');
const successRate = new Rate('success_rate');

export const options = {
  stages: [
    { duration: '2m', target: 10 },   // warmup
    { duration: '5m', target: 50 },   // main load
    { duration: '3m', target: 50 },   // sustain
    { duration: '2m', target: 0 },    // cooldown
  ],
  thresholds: {
    http_req_duration: ['p(95)<500', 'p(99)<1000'],
    http_req_failed: ['rate<0.001'],
    order_creation_duration: ['p(95)<800'],
    success_rate: ['rate>0.999'],
  },
};

const BASE_URL = __ENV.BASE_URL || 'https://api.example.com';

function authenticate() {
  const res = http.post(`${BASE_URL}/auth/token`, JSON.stringify({
    username: 'load_test_user',
    password: __ENV.TEST_PASSWORD,
  }), { headers: { 'Content-Type': 'application/json' } });

  check(res, { 'auth ok': (r) => r.status === 200 });
  return res.json('access_token');
}

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

  group('order flow', () => {
    // GET products
    const productsRes = http.get(`${BASE_URL}/api/products`, { headers });
    check(productsRes, { 'products 200': (r) => r.status === 200 });

    // POST order
    const start = Date.now();
    const orderRes = http.post(`${BASE_URL}/api/orders`, JSON.stringify({
      product_id: 'prod_001',
      quantity: 1,
    }), { headers });

    const duration = Date.now() - start;
    orderCreationDuration.add(duration);

    const ok = check(orderRes, {
      'order 201': (r) => r.status === 201,
      'order has id': (r) => r.json('id') !== undefined,
    });

    if (!ok) {
      errors.add(1);
      successRate.add(false);
    } else {
      successRate.add(true);
    }
  });

  sleep(1);
}

export function handleSummary(data) {
  return {
    'results/load_test_summary.json': JSON.stringify(data, null, 2),
    stdout: textSummary(data, { indent: ' ', enableColors: true }),
  };
}
Enter fullscreen mode Exit fullscreen mode

GitHub Actions Integration

Run load tests in CI with automatic artifact upload:

name: Load Test

on:
  workflow_dispatch:
    inputs:
      base_url:
        description: 'Target base URL'
        required: true
        default: 'https://staging.api.example.com'

jobs:
  load-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup k6
        uses: grafana/setup-k6-action@v1

      - name: Run load test
        env:
          BASE_URL: ${{ inputs.base_url }}
          TEST_PASSWORD: ${{ secrets.LOAD_TEST_PASSWORD }}
        run: |
          k6 run \
            --env BASE_URL=$BASE_URL \
            --env TEST_PASSWORD=$TEST_PASSWORD \
            --out json=results/raw.json \
            tests/load_test.js

      - name: Upload results
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: load-test-results
          path: results/
Enter fullscreen mode Exit fullscreen mode

Summary

The workflow:

  1. CLAUDE.md — define test profiles (smoke/load/stress/spike) and SLA thresholds once
  2. Staged ramp-up — gradual VU increase to avoid cold-start spikes skewing results
  3. Custom metricsTrend for latency, Counter for errors, Rate for success tracking
  4. Thresholds — k6 exits non-zero when SLA is breached, blocking the CI pipeline
  5. CI integrationworkflow_dispatch for on-demand runs, artifact upload for audit trail

Bottleneck detection becomes systematic: run the stress profile, watch where P99 spikes, correlate with the order_creation_duration trend to pinpoint the slow path.


Want Claude Code to review your load test setup and catch threshold misconfigurations before they reach production?

Check out the Code Review Pack (¥980) — prompts for systematic code review, security checks, and performance analysis:

👉 Code Review Pack → prompt-works.jp

Top comments (0)