Yashveer Singh
Connect
<- All posts
Performance Optimization12 min read

Why Your App Got Slower After You Added Users

An app slows down as it grows because the work per request scales with the size of the data, not the number of features. Queries that scanned ten rows now scan ten million. The fix is rarely a rewrite. It is finding the three or four queries and code paths that grew with the data and fixing those.

Written by Yashveer Singh, founder of Yashveer Labs.

What you actually need to know

  • The app slowed down because the cost of its work scales with the size of the data, not the number of features you shipped.
  • The database is almost always where the time goes. Measure before you guess.
  • The three usual suspects are a missing index, an N+1 query, and a connection pool that is too small.
  • A bigger server buys a few months and a bigger bill. Fixing the code buys years.
  • This is the technical version. For the business and human side of the same story, read the failure postmortem version of an app that got slower as it grew.
CauseHow it feelsCost to fixTypical impact
Missing indexOne page gets slow, then moreOne line, minutesOften 10x to 100x on that query
N+1 querySlowness scales with list sizeAn hour to refactorRemoves hundreds of queries per request
Small connection poolSlow only under loadA config changeRemoves connection wait time
No query cacheRepeated identical workHalf a dayCuts repeated read load
Undersized serverEverything is slightly slowMoney, monthlyHides the real cause

The core argument

The app did not get worse. The data got bigger. That is the whole story, and most teams miss it because the code review that introduced the slow query passed cleanly. The query was correct. It returned the right answer. It just happened to scan the whole table, and at a thousand rows that is free.

I have been called in to rescue more than one SaaS that was fast at launch and unusable at scale. The founders assumed they needed a rewrite or a famous database. They needed neither. They needed someone to turn on the slow query log and read it.

Performance at scale is not about clever code. It is about understanding which operations grow with your data and making sure those operations stay cheap. Almost everything else is noise. A function that runs in constant time stays fast forever. A query that scans a table gets slower every single day you stay in business.

The reason this feels mysterious is that the slowdown is gradual. There is no single deploy that broke it. The app degrades by one millisecond a day until one Tuesday the largest customer calls and the page does not load. By then the cause is buried under six months of unrelated commits.

Where the time actually goes

In every slow app I have profiled, the database is the majority of the request time. Not the framework. Not the language. Not the JSON serialization everyone loves to blame. The database.

Within the database, the cost concentrates in a small number of queries. The Pareto rule holds hard here. Three or four queries account for most of the pain. Find those and you have found the problem.

The fastest way to find them is the slow query log sorted by total time, which means per-call time multiplied by call frequency. A query that takes 50 milliseconds and runs 800 times per page is the real enemy, not the dramatic two second report nobody runs. Read the discipline of keeping a slow query log and you will catch these before customers do.

The second place time hides is the gap between issuing many small queries and issuing one good one. This is the N+1 query problem, and it is responsible for more "it got slow after we grew" tickets than any other single cause.

How long does it take to fix

FixTime to implementWhen to reach for it
Add a missing indexMinutesA specific query scans rows it should seek
Collapse an N+1 into a join or batchAn hour or twoQuery count scales with list size
Tune the connection poolA config changeSlowness appears only under concurrency
Add a read replicaA dayReads dominate and the primary is saturated
Introduce a cache layerHalf a dayThe same expensive read repeats constantly

The order matters. Indexes and N+1 fixes come first because they are cheap and the impact is enormous. Replicas and caches come later because they add operational weight and new failure modes.

What to measure before you change anything

  • The total time per request, split into database time and everything else.
  • The slowest queries by total time, not by single-call time.
  • The number of queries per request. If it grows with the data on the page, you have an N+1.
  • Connection pool wait time under peak load.
  • Cache hit rate if a cache already exists.

Expert opinion

The instinct when an app slows down is to add hardware, because hardware is a purchase order and a refactor is a conversation. I have watched teams triple their database instance and buy themselves six weeks. The query was still scanning the whole table. It just scanned it on a more expensive machine. Measure first. The fix is almost always smaller and cheaper than the panic suggests.

>

Yashveer Singh, founder of Yashveer Labs

How this played out on a real project

A SaaS client came to me with a dashboard that took eleven seconds to load for their biggest account and under a second for everyone else. The team was convinced the database had hit a wall and quoted themselves a painful migration.

I turned on the slow query log for an hour. The dashboard was running one query to load the customer's projects, then one query per project to count its tasks. Small accounts had five projects. The big account had nine hundred. That was nine hundred and one queries for one page load.

We replaced the loop with a single grouped query and added one index. The eleven seconds became under 400 milliseconds for the big account. No migration, no bigger server. The work took an afternoon, and the same pattern, once we knew to look, appeared in four other endpoints. For the deeper database angle, the team later read up on connection pooling as a quiet killer of performance.

Common mistakes

  1. Buying a bigger server before measuring. It hides the cause and doubles the bill.
  2. Optimizing the application code when the database is 80 percent of the time.
  3. Sorting the slow query log by per-call time and missing the frequent cheap query.
  4. Adding a cache over a broken query instead of fixing the query.
  5. Treating an N+1 as acceptable because it is fast in development with ten rows.
  6. Adding indexes blindly until writes slow down too. Index the queries you actually run.
  7. Assuming the slowdown needs a rewrite. It almost never does.

A first week plan to find and fix it

  1. Day one. Add request-level timing that splits database time from the rest. Confirm where the time goes.
  2. Day two. Turn on the slow query log. Sort by total time. Write down the top five queries.
  3. Day three. Find the N+1 patterns. Look for query counts that grow with the data on the page.
  4. Day four. Add the missing indexes for the top queries. Measure before and after with EXPLAIN ANALYZE.
  5. Day five. Collapse the worst N+1 into a single query. Re-measure the slow page.
  6. Ongoing. Set a backend performance budget so the next regression gets caught in review, not by a customer. The pattern is in how to set backend performance budgets.
FAQ

Frequently asked

Author

The work I take and why

I take work that compounds. I do not take work that is rework with extra steps. Yashveer Singh, founder of Yashveer Labs. If the topic on this page is what you are dealing with, the question is not whether it can be solved. It can. The question is whether you want to solve it once or four times. I am the person who solves it once.

Related reading