skills/infrastructure-performance/load-testing-commerce/SKILL.md
Simulate realistic shopper traffic on your checkout and catalog pages using k6 or Artillery to find performance bottlenecks before launch
npx skillsauth add finsilabs/awesome-ecommerce-skills load-testing-commerceInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Load testing e-commerce applications requires more than hammering an endpoint with concurrent requests. Realistic test scenarios simulate actual user behavior: browsing the catalog, searching for products, adding items to a cart, and completing checkout — including the think time between actions. This skill covers building realistic shopping scenarios in k6 and Artillery, interpreting results to find bottlenecks, and establishing performance baselines before major sales events.
| Platform | Load Testing Scope | Recommended Approach | |----------|-------------------|---------------------| | Shopify | Shopify's infrastructure scales automatically — you cannot overload it meaningfully | Test your theme's frontend performance with Google PageSpeed Insights and Lighthouse CI; test any custom apps or storefronts you host separately | | WooCommerce | You own the server — load testing is critical before sales events | Use Loader.io (free tier: 1 target, 10K connections/test) against your staging site; or k6/Artillery for detailed scenario testing | | BigCommerce | BigCommerce scales automatically — platform infrastructure is not a concern | Test your theme's frontend performance with PageSpeed Insights; test any custom middleware or headless layer you host | | Custom / Headless | Full control — load testing is your responsibility | Use k6 (open-source, scriptable) or Artillery (YAML-based) with realistic shopping scenarios against a staging environment |
Shopify's infrastructure handles virtually any traffic spike. Your load testing focus is the frontend experience:
Run Google PageSpeed Insights on your most critical pages (product page, collection page, checkout):
Check Shopify's built-in performance report:
fetchpriority="high" or compressionAudit your installed apps before a sale:
For Shopify Plus — notify Shopify support before major launches:
WooCommerce runs on your hosting infrastructure. Test against a staging environment that mirrors your production configuration (same PHP version, same MySQL version, same caching stack).
Quick load test with Loader.io (no code required):
Interpret results:
Before your test, ensure your staging stack is properly configured:
For custom storefronts, use k6 or Artillery against a staging environment that mirrors production.
Important before you start:
tok_visa) — never run load tests against real payment processors@test-load.invalid) so you can bulk-delete them afterk6 realistic shopping scenarios:
k6 uses a ramping-arrival-rate executor to control requests per second (more realistic than VU-based approaches). Typical e-commerce traffic distribution: 60% browse, 25% product detail, 10% cart, 5% checkout.
// k6/commerce-load-test.js
import http from 'k6/http';
import { check, sleep } from 'k6';
import { SharedArray } from 'k6/data';
const BASE_URL = __ENV.BASE_URL || 'https://staging.mystore.com';
const products = new SharedArray('products', function() {
return JSON.parse(open('./data/products.json')); // array of {id, defaultVariantId}
});
export const options = {
scenarios: {
catalog_browsing: {
executor: 'ramping-arrival-rate',
startRate: 0, timeUnit: '1s',
preAllocatedVUs: 50, maxVUs: 200,
stages: [
{ target: 60, duration: '2m' }, // Ramp up
{ target: 60, duration: '5m' }, // Steady state
{ target: 0, duration: '1m' }, // Ramp down
],
exec: 'catalogBrowsing',
},
checkout_flow: {
executor: 'ramping-arrival-rate',
startRate: 0, timeUnit: '1s',
preAllocatedVUs: 10, maxVUs: 50,
stages: [
{ target: 5, duration: '2m' },
{ target: 5, duration: '5m' },
{ target: 0, duration: '1m' },
],
exec: 'checkoutFlow',
},
},
thresholds: {
'http_req_duration{scenario:checkout_flow}': ['p(95)<3000'],
'http_req_failed{scenario:checkout_flow}': ['rate<0.01'],
'http_req_duration{scenario:catalog_browsing}': ['p(95)<1000'],
},
};
export function catalogBrowsing() {
http.get(`${BASE_URL}/api/collections/featured`, { tags: { step: 'homepage' } });
sleep(2 + Math.random() * 3); // Think time: 2–5s
const categories = ['t-shirts', 'hoodies', 'accessories'];
const category = categories[Math.floor(Math.random() * categories.length)];
http.get(`${BASE_URL}/api/collections/${category}?page=1&sort=popular`, { tags: { step: 'category' } });
sleep(3 + Math.random() * 5);
}
export function checkoutFlow() {
const product = products[Math.floor(Math.random() * products.length)];
// View product
const productRes = http.get(`${BASE_URL}/api/products/${product.id}`, { tags: { step: 'view_product' } });
check(productRes, { 'product page 200': r => r.status === 200 });
sleep(2 + Math.random() * 3);
// Add to cart
const cartRes = http.post(`${BASE_URL}/api/cart`, JSON.stringify({
items: [{ productId: product.id, variantId: product.defaultVariantId, quantity: 1 }],
}), { headers: { 'Content-Type': 'application/json' }, tags: { step: 'add_to_cart' } });
check(cartRes, { 'add to cart 200': r => r.status === 200 });
const cart = JSON.parse(cartRes.body);
sleep(1 + Math.random() * 2);
// Start checkout
const checkoutRes = http.post(`${BASE_URL}/api/checkout/start`, JSON.stringify({
cartId: cart.id,
customer: { email: `test-${Math.random().toString(36).slice(7)}@test-load.invalid` },
}), { headers: { 'Content-Type': 'application/json' }, tags: { step: 'start_checkout' } });
check(checkoutRes, { 'checkout start 200': r => r.status === 200 });
const checkout = JSON.parse(checkoutRes.body);
sleep(5 + Math.random() * 10); // Think time: filling form
// Place order
const orderRes = http.post(`${BASE_URL}/api/checkout/complete`, JSON.stringify({
checkoutId: checkout.id,
paymentToken: 'tok_visa', // Stripe test token
shippingMethodId: checkout.shippingMethods[0]?.id,
}), { headers: { 'Content-Type': 'application/json' }, tags: { step: 'place_order' } });
check(orderRes, { 'order placed 201': r => r.status === 201 });
}
Artillery YAML config (alternative to k6 — good for API-focused testing):
# artillery/commerce-load-test.yml
config:
target: "{{ $processEnvironment.BASE_URL }}"
phases:
- name: "Warm-up"
duration: 60
arrivalRate: 5
- name: "Ramp up"
duration: 120
arrivalRate: 5
rampTo: 50
- name: "Peak load"
duration: 300
arrivalRate: 50
- name: "Spike"
duration: 30
arrivalRate: 200
- name: "Recovery"
duration: 60
arrivalRate: 20
processor: "./processors/commerce-helpers.js"
scenarios:
- name: "Browse catalog"
weight: 60
flow:
- get:
url: "/api/collections/all?page=1"
capture:
json: "$.products[0].id"
as: "productId"
- think: 3
- get:
url: "/api/products/{{ productId }}"
- name: "Complete checkout"
weight: 10
flow:
- function: "generateCheckoutData"
- post:
url: "/api/cart"
json:
productId: "{{ productId }}"
quantity: 1
capture:
json: "$.id"
as: "cartId"
- think: 8
- post:
url: "/api/checkout/start"
json:
cartId: "{{ cartId }}"
email: "{{ email }}"
capture:
json: "$.checkoutId"
as: "checkoutId"
- think: 10
- post:
url: "/api/checkout/complete"
json:
checkoutId: "{{ checkoutId }}"
paymentToken: "tok_visa"
expect:
- statusCode: 201
Run the test and capture a baseline:
k6 run \
--env BASE_URL=https://staging.mystore.com \
--out json=results/baseline-$(date +%Y%m%d).json \
k6/commerce-load-test.js
Analyze results per step using tags:
# Overall summary (k6 outputs this automatically at end of run)
# Per-step breakdown from the JSON output
cat results/baseline-*.json | jq '
[.data_points[] | select(.type=="Point" and .metric=="http_req_duration")]
| group_by(.tags.step)
| map({step: .[0].tags.step, p95_ms: (map(.value) | sort | .[length * 0.95 | floor])})
'
Scale targets for e-commerce:
Run load tests in CI before major releases (GitHub Actions):
# .github/workflows/load-test.yml
name: Load Test (Pre-Release)
on:
workflow_dispatch:
inputs:
target_url:
description: 'Staging URL to test'
required: true
default: 'https://staging.mystore.com'
jobs:
load-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- 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: Run load test
env:
BASE_URL: ${{ github.event.inputs.target_url }}
run: k6 run --env BASE_URL=$BASE_URL --out json=results.json k6/commerce-load-test.js
- uses: actions/upload-artifact@v4
with:
name: load-test-results
path: results.json
tok_visa or equivalent test tokens; never run load tests against real payment processors@test-load.invalid) so you can bulk-delete test data after runs: DELETE FROM orders WHERE email LIKE '%@test-load.invalid'| Problem | Solution |
|---------|----------|
| Checkout scenario fails because products are sold out | Reserve test products with unlimited inventory; use a separate is_load_test_product flag and filter them from real catalog pages |
| Loader.io test passes but production still slows down | Staging may not mirror production load — confirm Redis Object Cache is active, MySQL version matches, and the server size is the same |
| k6 VUs exhausted before reaching target RPS | Use ramping-arrival-rate executor (controls RPS) instead of ramping-vus (controls concurrent users); increase preAllocatedVUs and maxVUs |
| Alert noise during planned load tests | Add a load test flag to your monitoring system (Datadog tag, Grafana annotation) to suppress non-critical alerts during the scheduled test window |
| Results not reproducible between runs | Use a SharedArray with a fixed dataset file instead of Math.random() product generation; fix the test data set before the run |
tools
Let shoppers save products to a wishlist, share it with friends, and get notified when saved items come back in stock or drop in price
development
Build a themeable storefront with design tokens and CSS custom properties that supports white-labeling, multi-brand variants, and dark mode
development
Speed up product discovery with instant search suggestions, fuzzy typo matching, and category-aware results powered by Algolia or Elasticsearch
development
Build a mobile-first storefront with thumb-friendly navigation, sticky add-to-cart buttons, and touch-optimized components for high mobile conversion