Junior Level · Learn by Doing

Performance Testing Basics

Users abandon slow websites. Every second matters. Learn to measure page load times, identify bottlenecks, and spot performance bugs using browser DevTools.

Junior ~10 min read

The Hook — The Two-Second Rule

A Wellington SaaS company launched a new checkout flow. The page was feature-complete, validation worked, and data was correct. But checkout took 4.2 seconds to load. Within a week, their cart abandonment rate jumped from 8% to 22%. Users weren't seeing bugs. They were leaving because the page felt slow.

Performance is not optional. A slow page is a failing page. Users don't read error messages; they hit the back button. Performance testing is not "we'll optimise later." It is QA. It is finding problems before they reach production.

The Rule

Performance testing measures how fast a website or app responds to user actions. It covers:

  • Page load time: How long until the page is interactive? (Target: < 3 seconds)
  • Core Web Vitals: Largest Contentful Paint (LCP), First Input Delay (FID), Cumulative Layout Shift (CLS)
  • Interaction response: How long to respond when a user clicks a button? (Target: < 100ms)
  • Memory usage: Does the app leak memory or grow unbounded?
  • Network bottlenecks: Which assets are largest or slowest?

Key Metrics (No Tools Needed, Just Browser)

Largest Contentful Paint (LCP): When the biggest element on the page becomes visible. If it takes 5+ seconds, users see a blank screen and assume the page is broken.

First Input Delay (FID): Time between the user clicking a button and the app responding. If it's > 100ms, the interaction feels sluggish.

Cumulative Layout Shift (CLS): How much does the page jump around while loading? If elements move unexpectedly, users might click the wrong thing.

Load vs Stress vs Soak Testing

Performance testing comes in three flavours, each revealing different problems:

  • Load Testing: Run your normal expected load. Example: an e-commerce checkout page expecting 100 concurrent users during business hours. Measure response time and error rate at normal volume. Goal: confirm the app stays fast under expected conditions.
  • Stress Testing: Gradually increase load until the system breaks. Example: ramp from 100 to 500 to 1,000 concurrent users and see where things fail. Goal: find the breaking point so you know your capacity ceiling.
  • Soak Testing: Run a realistic load for a long time (8-24 hours). Example: simulate 200 concurrent users for 12 hours. Goal: detect memory leaks, connection pool exhaustion, and gradual degradation that wouldn't show up in a 5-minute test.

Real Example: E-commerce Checkout Under Load

A NZ online retailer runs a load test during the Boxing Day sale. Expected: 200 concurrent users checking out. They run a 10-minute test simulating 200 users clicking "Pay Now." Results:

  • Average response time: 1.2 seconds
  • 95th percentile (p95): 3.1 seconds
  • 99th percentile (p99): 8.7 seconds
  • Error rate: 0.1% (1 out of 1000 requests fails)
  • Conclusion: acceptable for normal load, but p99 is concerning. If a user is in the slowest 1%, they wait 8.7 seconds.

Reading a Performance Report: Throughput, Response Time, Error Rate

Throughput: How many requests per second does the server handle? A healthy server processing 500 concurrent users should show throughput of ~100 requests per second (all of them completing in under 5 seconds). If throughput drops while load increases, the server is getting saturated.

Response Time: Never look at average alone. Always look at percentiles (p50, p95, p99). The average might be 500ms, but if p99 is 10 seconds, you have users experiencing slow interactions.

Error Rate: What percentage of requests fail? 0.1% is acceptable; 1% is concerning; 5%+ means the system is overwhelmed. Errors include timeouts, 500 errors, and connection resets.

Understanding Response Time Percentiles

Percentiles are critical for understanding user experience:

  • p50 (median): The time 50% of users experience. If p50 = 500ms, half your users get the page in 500ms or faster.
  • p95: The time 95% of users experience. If p95 = 1.5s, 95% of users load the page in 1.5s or faster. But 5% wait longer.
  • p99: The time 99% of users experience. If p99 = 8s, that last 1% (1 out of 100 users) waits 8 seconds. On a busy site, that's significant.

Example: A SaaS app reports p50=300ms, p95=1200ms, p99=5000ms. The app feels fast for most users (p50), acceptable for most (p95), but painful for the slowest 1% (p99). If you want a fast experience, focus on lowering p99.

Example: Bad LCP

User opens a NZ news website. For 6 seconds, the page is blank. Then the headline appears. LCP = 6 seconds. The user is frustrated and leaves.

Why? The hero image is 8MB and the CDN is slow. Solution: compress the image, or load placeholder text while the image downloads.

Measuring Performance with DevTools

Step 1: Open DevTools (F12)

Step 2: Go to Performance tab

Step 3: Click the record circle, then reload the page. Click the circle again to stop.

You will see:

  • Blue line (DOMContentLoaded): When the HTML and CSS are parsed. Usually < 2s.
  • Red line (Load): When all resources (images, scripts) are downloaded. Often 3–5s.
  • LCP marker: The green vertical line showing when the largest element became visible.

Real Performance Test Scenario: E-commerce Product Page

Setup: A team wants to test if their product page stays fast when it gets 1,000 concurrent visitors during a flash sale.

Test plan:

  1. Simulate 100 users, ramp up to 1,000 over 10 minutes (load test)
  2. Each user loads the product page 5 times with a 30-second think time between loads
  3. Measure: page load time, image load time, "Add to Cart" button response time, error rate
  4. Run for 30 minutes total

Results:

  • Average load time: 2.1s (target: < 3s, PASS)
  • p95 load time: 4.2s (concerning—5% of users experience slow loads)
  • Image load time: 1.8s average, p95 = 3.4s (images are the bottleneck)
  • "Add to Cart" response: 150ms average (acceptable)
  • Error rate: 0.2% (1 in 500 requests timeout)

Actions: Images need optimization. Compress them, add lazy loading, or move them to a faster CDN. The "Add to Cart" endpoint is fine; focus on static asset delivery.

Simple k6 Load Test Example

k6 is a lightweight load testing tool that lets you simulate users in code. Here's a minimal example for testing an e-commerce checkout:

import http from 'k6/http';
import { check, sleep } from 'k6';

export let options = {
  stages: [
    { duration: '2m', target: 100 }, // ramp up to 100 users
    { duration: '5m', target: 100 }, // stay at 100
    { duration: '2m', target: 0 },   // ramp down
  ],
};

export default function () {
  // Step 1: Load the product page
  let res = http.get('https://shop.example.com/product/123');
  check(res, {
    'product page loads': (r) => r.status === 200,
    'page loads in < 3s': (r) => r.timings.duration < 3000,
  });

  // Step 2: Wait 5 seconds (user thinking time)
  sleep(5);

  // Step 3: Click "Add to Cart"
  res = http.post('https://shop.example.com/cart', {
    product_id: 123,
    quantity: 1,
  });
  check(res, {
    'add to cart succeeds': (r) => r.status === 200,
    'add to cart responds in < 200ms': (r) => r.timings.duration < 200,
  });
}

Tools Overview: k6, JMeter, Gatling

  • k6: Code-first load testing. Write tests in JavaScript. Lightweight, fast, good for CI/CD. Start here if you're new to load testing.
  • Apache JMeter: GUI-based load testing. No coding required. Good for teams without developers. Steeper learning curve but very flexible.
  • Gatling: High-performance load testing. Code-based (Scala). Best for large-scale stress testing (10,000+ concurrent users). More complex to set up.

Common Performance Pitfalls

Pitfall 1: Uncompressed assets. A 5MB hero image loads on a 4G connection in 30+ seconds. Compress to < 200KB with proper image formats.

Pitfall 2: Blocking JavaScript. A large JavaScript file (500KB) prevents the page from rendering until it downloads. Use code splitting and lazy loading.

Pitfall 3: N+1 database queries. Showing a list of 10 products triggers 11 queries (one for the list, one per product). Use batch queries or joins instead.

Pitfall 4: No caching. The same API endpoint is called 100 times per second. Use HTTP caching (ETag, Last-Modified) or a cache layer (Redis) to avoid redundant computation.

Pitfall 5: Memory leaks in long-running services. A soak test reveals that memory grows from 500MB to 2GB over 24 hours. Investigate event listeners, timers, and global variables that are never cleaned up.

What to Watch For

  • Long task (yellow block): JavaScript running for > 50ms blocks user input. Look for slow scripts.
  • Render (purple block): Recalculating layout and painting to screen. Long renders mean heavy CSS or DOM manipulation.
  • Layout shift (orange): Elements moving unexpectedly. Causes misclicks.

Network tab (to spot large assets):

  • Open DevTools → Network tab
  • Reload the page
  • Look at the "Size" column — images should be < 200KB each
  • Look at the "Time" column — assets should download in < 1 second on 4G
  • If one image is 5MB and takes 10 seconds, that's a problem

Common Mistakes

❌ Testing Only on a Fast Connection

Your office has gigabit Wifi. A rural NZ customer on 4G loads the same page in 15 seconds. Always throttle your network: DevTools → Network → choose "Fast 3G" or "Slow 3G" before measuring.

❌ Testing Only on a High-End Device

Your MacBook with 16GB RAM loads instantly. A budget Android phone with 2GB RAM struggles. DevTools can throttle CPU (6x slowdown) to simulate a slower device.

❌ Not Clearing Cache

First load is slow; second load is fast (cached). Always disable cache in DevTools (checkbox in Network tab) or open in incognito mode to test fresh load performance.

❌ Ignoring Interaction Performance

Page loads in 2 seconds but clicking a button takes 5 seconds to respond. That's a performance bug. Test not just page load, but button clicks, form submissions, and scrolling.

Self-Check

  1. What does LCP stand for and why does it matter?
    Largest Contentful Paint. It's when the biggest element on the page becomes visible. If it takes > 4 seconds, users think the page is broken.
  2. You measure page load at 2 seconds on your office Wifi. A user on 4G reports it takes 20 seconds. Why?
    You tested on a fast connection. Always use DevTools Network Throttling (Fast 3G or Slow 3G) to simulate real-world conditions.
  3. The page loads in 3 seconds but feels slow when scrolling. What might the problem be?
    Layout shift or janky rendering. Look at the Performance tab for long render blocks (purple) or layout shifts (orange). JavaScript may be blocking the main thread.
  4. An image on the page is 12MB and takes 15 seconds to load. What should you report?
    Performance bug. Images should be < 200KB and load in < 1 second on 4G. The image needs compression or the CDN needs optimisation.
  5. You reload the page and it feels fast. You reload again and it feels slower. Why?
    Cache. First load is fresh (no cache). Second load is cached (fast). Always disable cache in DevTools when testing.
  6. You run a load test with 100 concurrent users. Average response time is 800ms, but p99 is 6 seconds. What does this mean?
    99% of users experience acceptable load times, but the slowest 1% wait 6 seconds. On a busy site with 10,000 users, that's 100 users frustrated. Focus on reducing p99, not just the average.
  7. A soak test runs for 8 hours and response times start fine but degrade to 10+ seconds by hour 7. What's the likely cause?
    Memory leak. The app is not cleaning up resources (event listeners, DOM elements, timers). Memory fills up, garbage collection takes longer, and performance degrades. Check for listeners that are never removed and objects that grow over time.
  8. You're testing an API endpoint. Throughput is 50 requests per second with 200 concurrent users. But when you increase to 500 concurrent users, throughput drops to 30 requests per second. Why?
    The server is becoming saturated. It can't handle the increased load, so more requests queue up and timeouts occur. This is your system's breaking point.