← Back to Insights

Scaling decisions

How to Prepare for 10x Traffic

Written by:Better SoftwareBetter Software TeamUpdated 10 min read
How to Prepare for 10x Traffic

Quick Answer

Preparing a product for 10x traffic starts with measurement, not a new architecture. Teams that handle growth well set baselines, load test to find the first resource that fails, then fix bottlenecks in order, usually database queries and connections, caching, slow synchronous work and stateless horizontal scaling. Overload protection such as rate limits and degraded responses comes next. Microservices and speculative resilience work rarely belong on that first list.

When a founder tells us traffic could grow tenfold, the instinct in the room is often to redesign the system. We think that instinct is usually wrong. Preparing for 10x traffic means finding the specific resources that will run out first under higher load and removing those limits one at a time, while leaving the parts of the system that are not under pressure alone.

Over-engineering is the opposite failure, where a team spends months on architecture for load that may never arrive. This post covers why early systems hit limits where they do, how to measure and load test, the order we fix bottlenecks in, how to protect the system when load exceeds capacity, a worked example and the mistakes that waste the most engineering time.

Why Early Products Hit Traffic Limits Where They Do

Early products are built for speed of development, and the patterns that suit low traffic become the first points of contention as load grows. A single deployable, direct database access and synchronous processing are all sensible choices at the start. We would make most of them again.

The database is often the first limit, through connection counts or slow queries. PostgreSQL, for example, notes that its default is typically 100 connections, and a fleet of new application instances can exhaust that long before CPU becomes a problem. Application servers run out of threads or memory next, and slow third-party calls hold requests open under load.

None of this calls for a rewrite. It calls for knowing which of these limits you will hit first at your traffic, which only measurement can tell you.

Measure and Load Test Before Changing Anything

Start by instrumenting what you already run. We track CPU, memory, disk and network on each layer, database query times and connection counts, and response times and error rates for the main user flows.

Then load test on a staging environment that resembles production. Grafana's k6 documentation distinguishes average-load tests from stress tests, which assess how a system performs at its limits when load exceeds the expected average, and from spike tests for sudden surges.

We usually begin at 2x current traffic and step up from there. The goal is to find the first resource that saturates and the load at which it happens. That resource is your real scaling limit, and fixing anything else first is guesswork.

Write the result down as a short capacity note. It should name the traffic level tested, the first resource to saturate, the response time and error rate at that point and the change you plan to make. When we repeat the test after each fix, that note becomes a record of how much headroom each change bought, which makes the next prioritization decision much easier to defend.

Fix 10x Traffic Bottlenecks in This Order

Fix bottlenecks in the order the load test reveals them, which for most early products means the database, then caching, then slow synchronous work, then horizontal scaling. We work through six areas, and we stop as soon as the measured headroom covers the traffic we expect.

1. Tune the Slowest Database Queries

Review the queries that take the most total time, not just the slowest single ones. Add the missing indexes, rewrite the worst joins and remove queries that run in loops.

This is usually the cheapest large gain available. Read replicas can help heavy read workloads, but only after the queries themselves are sound.

2. Pool and Cap Database Connections

Put a connection pool between the application and the database, and size it to what the database can actually serve. Adding instances without pooling multiplies connections and can take the database down faster than the traffic would.

Watch connection wait time in the load test. If requests queue for connections, the database is your limit, whatever the CPU graphs say.

3. Cache Data That Rarely Changes

Cache data that is read often and changes rarely, such as configuration, reference data and expensive computed views. Start with the simplest layer that works, then add a shared cache when several instances need the same data.

Every cache needs an invalidation rule written down before it ships. We cover the trade-offs in why caching strategy matters more than most founders think.

4. Move Slow Work Off the Request Path

Anything slow that the user does not need to wait for should leave the request, including file processing, emails, report generation and calls to slow third parties. Put it on a queue and let separate workers handle it.

That keeps response times flat as load grows and lets the heavy work scale on its own. Our post on when to introduce a queue system into your architecture covers when that step is worth it.

5. Make the Application Stateless and Add Instances

Once a single instance is efficient, scale out by running more of them behind a load balancer. That only works if no session state or files live on one machine.

Horizontal scaling of a stateless application is the most predictable way to absorb more traffic. Vertical scaling buys time, but it has a ceiling and it hides inefficiencies.

6. Speed Up Releases and Rollbacks

Under higher traffic, a bad release hurts more people faster. Automated deployments and a rollback that takes minutes reduce the cost of every change you make while scaling.

We treat fast rollback as part of capacity work, because it limits how long any mistake stays in front of the larger audience.

Protect the System When Traffic Exceeds Capacity

Even a well-tuned system will eventually see more traffic than it can serve, so we plan what it does at that point. Google's SRE book says a well-behaved backend should accept only the requests that it can process and reject the rest gracefully, and it describes serving degraded responses that are cheaper to compute.

For an early product, that translates into a short list.

  • Rate limits at the edge or in the application, so one client cannot consume everyone's capacity.
  • Clear priority for the core user flows, so sign-up and checkout keep working when reports slow down.
  • Degraded modes for non-essential features, such as hiding recommendations under heavy load.
  • Timeouts on every outbound call, so a slow dependency cannot hold your request threads.

These are small changes with an outsized effect on how a traffic spike feels to users. They are also far cheaper than the fault-tolerance machinery some teams build for failures they have never observed.

A Worked Example With Image Uploads

Here is a hypothetical example, not a client story, of a product that needed more headroom. A SaaS product accepts user image uploads, saves them to local disk and processes them in a single worker inside the request.

A load test at several times current traffic shows rising latency, a saturated disk and high CPU from image processing. Database connections also climb, because each upload triggers profile updates.

The team makes five targeted changes. Uploads stream straight to object storage, processing moves to a queue with separate workers, an index is added to the profile table, the API returns an accepted response immediately and the interface polls for completion.

The system absorbs the projected traffic with the same overall architecture. We would call that a good outcome, because every change answered a measured limit and none of it required a new platform.

Mistakes That Waste the Most Engineering Time

The most expensive scaling mistakes we see are architectural changes made without measurements. Common ones include the following.

  • Splitting into microservices to handle traffic that a tuned single deployable could serve, which adds distributed-system problems without removing the bottleneck.
  • Upgrading hardware repeatedly instead of fixing the query or the synchronous call that is actually slow.
  • Optimizing code paths that the load test showed were not under pressure.
  • Building elaborate failover for failure modes nobody has observed.
  • Scaling without monitoring, which makes the next limit a surprise and incidents hard to diagnose.

If the architecture question does come up, we cover it in the monolith vs microservices decision for early-stage products. For most products preparing for 10x traffic, the honest answer is to keep the monolith and fix what the load test found, which is the core of how we approach product engineering for early-stage teams.

Plan for 10x Traffic With Measured Steps

To prepare for 10x traffic without over-engineering, measure first, load test to find the first limit, fix bottlenecks in the order they appear and add overload protection before you need it. Stop when the measured headroom covers the traffic you expect.

We would rather see a team run three load tests than one redesign. If you want help finding your real limits before a launch or a big customer arrives, Better Software can work through it with you.

Frequently Asked Questions

How to Scale a Web Application to Handle Increased Traffic?

We would measure first, then load test to find the first resource that fails. Fix that limit, usually database queries or connections, then add caching, move slow work to queues and run more stateless instances behind a load balancer. Repeat until the headroom covers your expected traffic.

How Do Websites Scale?

Websites scale by removing the limit that fails first and then adding capacity. That usually means efficient database access, caching, background workers for slow tasks and many identical stateless servers behind a load balancer. We add rate limits and degraded modes so overload fails gracefully.

How Often Should Load Testing Be Done?

We run load tests before any expected traffic jump, such as a launch, a campaign or a large customer, and after major architecture or database changes. Many teams also run a lighter test on a regular release schedule so performance regressions show up before users notice them.

What Is Performance Testing and Load Testing?

Performance testing is the broad practice of measuring speed, stability and resource use. Load testing is one type, which applies expected or higher traffic to see how the system behaves. We also run stress and spike tests to find the breaking point and the response to surges.

When Is Load Testing Required?

We treat load testing as required before any event that could multiply traffic, such as a launch, a press moment or onboarding a large account. It also matters after changes to the database, caching or infrastructure, because those are where capacity limits usually move.

About the author

Better Software

Better Software Team

Product and engineering team

Better Software Team is the product and engineering team at Better Software. We build new software products and improve existing applications and systems, with deeper experience in healthcare, finance and energy.