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.
| Cause | How it feels | Cost to fix | Typical impact |
|---|---|---|---|
| Missing index | One page gets slow, then more | One line, minutes | Often 10x to 100x on that query |
| N+1 query | Slowness scales with list size | An hour to refactor | Removes hundreds of queries per request |
| Small connection pool | Slow only under load | A config change | Removes connection wait time |
| No query cache | Repeated identical work | Half a day | Cuts repeated read load |
| Undersized server | Everything is slightly slow | Money, monthly | Hides 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
| Fix | Time to implement | When to reach for it |
|---|---|---|
| Add a missing index | Minutes | A specific query scans rows it should seek |
| Collapse an N+1 into a join or batch | An hour or two | Query count scales with list size |
| Tune the connection pool | A config change | Slowness appears only under concurrency |
| Add a read replica | A day | Reads dominate and the primary is saturated |
| Introduce a cache layer | Half a day | The 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
- Buying a bigger server before measuring. It hides the cause and doubles the bill.
- Optimizing the application code when the database is 80 percent of the time.
- Sorting the slow query log by per-call time and missing the frequent cheap query.
- Adding a cache over a broken query instead of fixing the query.
- Treating an N+1 as acceptable because it is fast in development with ten rows.
- Adding indexes blindly until writes slow down too. Index the queries you actually run.
- Assuming the slowdown needs a rewrite. It almost never does.
A first week plan to find and fix it
- Day one. Add request-level timing that splits database time from the rest. Confirm where the time goes.
- Day two. Turn on the slow query log. Sort by total time. Write down the top five queries.
- Day three. Find the N+1 patterns. Look for query counts that grow with the data on the page.
- Day four. Add the missing indexes for the top queries. Measure before and after with EXPLAIN ANALYZE.
- Day five. Collapse the worst N+1 into a single query. Re-measure the slow page.
- 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.
Frequently asked
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.
Posts that line up with this one.
- Performance Optimization
Image Optimization at Scale: AVIF, WebP, Responsive Images
Images are the largest contributor to page weight on most web products. Here is the format selection, responsive image, and delivery strategy that cuts load time without manual work.
- Performance Optimization
INP: The New Core Web Vital Most Teams Are Failing
Interaction to Next Paint replaced First Input Delay in 2024 and it is harder to pass. Most teams have not caught up. Here is what INP measures, why it matters, and how to fix the common failure patterns.
- Performance Optimization
Largest Contentful Paint: The Metric That Changes Conversions
LCP is the Core Web Vital that measures how fast the main content loads. It is also the metric most directly correlated with conversion rate. Here is what causes poor LCP and how to fix it systematically.
- Performance Optimization
Lazy Loading: The Patterns That Work and the Ones That Backfire
Lazy loading reduces initial page weight when done correctly. When done incorrectly, it delays the content users actually need and hurts Core Web Vitals. Here is how to apply it with precision.