Back to Blog
Lesson 43 of the System Design: System Design Fundamentals course
ArchitectureAugust 29, 20264 min read

Load Testing Your Prototype: Identifying Breaking Points

Learn to use load testing to validate your system's performance. Discover how to simulate traffic, analyze latency, and find your architecture's limits.

load testingperformancebenchmarkingstress testingsystem design
Crumpled papers scattered around with a note reading 'Take a Break'.

Previously in this course, we successfully achieved End-to-End Prototype Integration: Validating System Functionality, ensuring our services communicate correctly. Now that your prototype is functional, the next step is to ensure it remains stable under real-world conditions. This lesson focuses on load testing—the practice of intentionally bombarding your system with traffic to verify that your design holds up under pressure.

Understanding Load Testing from First Principles

In system design, a "prototype" often works perfectly when you are the only user. However, concurrency changes everything. Load testing is not just about measuring speed; it is about finding the "knee" in the curve—the point where your system's latency spikes or its error rate climbs, indicating that resources (CPU, memory, connections) are exhausted.

We categorize this into three activities:

  1. Load Testing: Simulating expected peak traffic to ensure baseline performance.
  2. Stress Testing: Pushing traffic beyond the expected peak to find the system’s breaking point.
  3. Soak Testing: Running a sustained load over hours to detect memory leaks or resource degradation.

Worked Example: Stress Testing with k6

We will use k6, an open-source tool written in Go that uses JavaScript for scripting, which makes it ideal for developers.

First, ensure you have k6 installed. Create a file named load-test.js:

JAVASCRIPT
import http from CE9178">'k6/http';
import { check, sleep } from CE9178">'k6';

export const options = {
  stages: [
    { duration: CE9178">'30s', target: 20 }, // Ramp up to 20 users
    { duration: CE9178">'1m', target: 20 },  // Stay at 20 users
    { duration: CE9178">'30s', target: 0 },  // Ramp down
  ],
};

export default function () {
  const res = http.get(CE9178">'http://your-prototype-api.local/data');
  check(res, { CE9178">'status was 200': (r) => r.status === 200 });
  sleep(1);
}

What this code does:

  • stages: Defines the traffic pattern. It simulates a gradual ramp-up rather than an instant "thundering herd."
  • check: Validates that our server is actually returning successful responses, not just crashing silently.

Run this with: k6 run load-test.js.

Analyzing Performance Under Stress

When the test completes, k6 outputs a summary. As an engineer, you should focus on these three metrics:

MetricMeaningWhat it tells you
http_req_durationRequest latencyHow slow the system feels to the user.
http_req_failedError rateThe stability limit of your service.
iterationsThroughputHow many requests per second (RPS) you can handle.

If http_req_duration (the p95 or p99) starts climbing sharply while iterations plateaus, you have found your breaking point. This indicates the system is queuing requests because it cannot process them faster.

Hands-on Exercise

  1. Baseline: Run the script above and record the average response time.
  2. Increase: Modify the target in the stages section to 50. Run the test again.
  3. Observe: Did the error rate increase? If the system stays at 200 OK but gets slower, you are hitting a CPU or database bottleneck. If you see 5xx errors, you are likely hitting a connection pool limit or a thread exhaustion issue.

Common Pitfalls

  • Testing from the same machine: Running the load generator on the same server you are testing consumes the very resources the app needs. Always run your load test from a separate machine (or container).
  • Ignoring the warm-up: If you slam a cold cache with max traffic, you’ll get a false negative. Always include a ramp-up period to let the system "warm up."
  • Forgetting to measure the database: Often, the application code is fine, but the database connection pool is full. Always correlate app-level metrics with database CPU/connection usage.

Frequently Asked Questions

Q: At what point is a system "broken"? A: A system is broken when it fails to meet your defined non-functional requirements (e.g., if you promised <200ms latency but are now at 2s).

Q: Should I automate these tests? A: Yes. Integrate these into your CI/CD pipeline to ensure that new code doesn't drastically regress performance.

Recap

Load testing is the only way to move from "it works on my machine" to "it works in production." By using tools like k6 to perform stress testing, you can identify the exact breaking point of your architecture, allowing you to optimize specifically where it matters most.

Up next: Analyzing Resource Bottlenecks — we will dive deep into how to interpret your findings to optimize the specific components causing your performance degradation.

Similar Posts