Skip to main content
Handbook/Scale/Page 144 · Bottlenecks

Finding the Limits

Share

Share this page

Pass it to someone who needs it.

Star on GitHub

Key takeaway: Find the real bottleneck

Chapter 15 of 15 · Scale

Grow it to handle real traffic and data

You made it. The app is live and running; now it grows. This last part is how you handle more users and more data without blowing the budget or the database, and how to know when to bring in a real pro.

By the end of this part, you can:

  • Find and fix what slows your app down
  • Scale to more traffic without wasting money
  • Grow your database without it falling over
  • Know when a problem needs a human expert
Done:Operate·You are here: Scale

Your app is live and healthy: shipped, monitored, running without you babysitting it. Now it is growing, more users and more data arriving at once, and something in it will buckle first. That part is almost never the one you would guess. This chapter gets you to find the one real limit before you change a single line.

15.1.1A bottleneck caps the whole system

A bottleneck is the single slowest part that caps everything around it, the narrowest point in the pipe. Water moves only as fast as the tightest section allows, no matter how wide the rest of the pipe is.

Your app works the same way. A request that touches ten parts is only as fast as its slowest one. The system's real speed is set by that single part, not by the average of the parts. The same goes for its throughput, how much work it can handle at once.

15.1.2One ordinary server goes further than you think

Get the scale straight before you touch any of this. One modest server with a well-indexed database routinely serves hundreds of requests a second, which is millions a day, far more traffic than most products ever see.

So if you have a few hundred or a few thousand users and something feels slow, you are almost certainly not out of capacity. You have one slow query, and the heavier moves later in this part are insurance you buy at a size you can measure, not imagine.

15.1.3Measure before you optimize

The rule that saves you the most wasted effort: never guess at what is slow. Slowness rarely lives where it feels like it does, so a day spent speeding up the wrong part moves nothing. You met this rule tuning page speed; at scale it governs the whole system, not one screen.

The part that buckles under load is usually invisible from the outside: a database query, not the code you stare at. So measure first: put the app under realistic load, many requests at once, not the single idle click you test by hand. That is the same load-test tool from the testing part, k6, pointed at the slow path instead of at your launch peak.

On a live app you often do not need to simulate anything. The real traffic is already hitting you, so read where the time goes from the traces and logs you already have from making the app observable. Only synthesize load when you need to push past what today's users produce.

15.1.4Find the slowest part

Three tools turn "it feels slow" into a number. A profiler breaks a single request into timed spans and shows how long each part took. Simple timing does the same by hand. The slow query log is the database's own record of every query that ran too long, and it is where scale problems hide most often.

Run one and you get something like this:

GET /orders 842 ms total auth check 3 ms render page 11 ms db: list all orders 814 ms <- bottleneck db: look up user 9 ms

One span dominates. The 814 ms query is the bottleneck; everything else is rounding error. You are not guessing anymore, you can see it.

15.1.5Fix the one that matters

Fix that one query and nothing else. It is tempting to tidy the render step or the auth check while you are in there. But do the math: make everything except the 814 ms query twice as fast, and you save a handful of milliseconds out of eight hundred.

Rule of thumb: speeding up anything but the bottleneck barely changes the total. Finding it beats optimizing on instinct every time.

Then measure again. The bottleneck does not vanish, it moves: fix the query and the next-slowest part becomes the new ceiling. Repeat until the app is fast enough for the load you have, then stop.

Find the slowest part, fix only it, measure again; the bottleneck just moves.

The rest of this part is how you fix each kind of bottleneck you find; this prompt finds the first one without letting your agent guess:

Ready prompt
Act as a senior engineer hunting a performance bottleneck. Do not guess and do not optimize on instinct. Work from measurement only. Read my performance targets and my service levels first, and measure with the instrumentation, logs, and dashboards I already have before adding any new tooling. 1. Reproduce the slowness under realistic load, not a single idle request. State how you load it and what you measured. 2. Profile it: break the slow path into timed parts (request spans, function timing, the database slow query log). Show where the time actually goes. 3. Rank the parts by share of total time. Name the single slowest one. That is the bottleneck. 4. Fix only that one. Explain the change and why it attacks the real cause, not a symptom. 5. Measure again and show before and after. Say what the new slowest part is, and whether it still misses my targets or is fast enough. Report numbers, not adjectives. If you cannot measure a step, say so instead of guessing. Record the fix and its numbers in my decision log. If you need the full reasoning behind this step, read https://zalt.me/guides/vibe-coding/scale/bottlenecks What feels slow and when:

Do this now: pick the one page or action your users call slowest, paste the prompt, and let your agent measure where the time actually goes before you change a single line.

Mahmoud Zalt

Mahmoud Zalt

Software engineer, 16+ yrs · built Sistava.com in 3 months, idea to production, using these methods

Resources
Star on GitHubContribute
Donate

Support my work

A small tip keeps the free work coming.

© 2026 Mahmoud Zalt. Free to read, not to republish.
Copyright & license