TL;DR: AI coding tools write queries that work fine with a handful of test rows. Two patterns cause almost every database slowdown after launch. The first is N+1 queries: one query per row, instead of one query total. The second is missing indexes on the columns your app filters by most. Fix both. Add connection pooling and short transactions too. Most database performance problems go away.

Your app worked fine in testing. Ten test rows, one test user, everything fast. Then real users show up, the table grows, and pages that used to load in a blink now take seconds. Nothing crashed. Nothing looks broken in the code. The database is just doing far more work than it needs to.

This happens to AI-built apps more than most, because the code that works with ten rows and the code that works with ten thousand rows look almost identical on the page. The difference only shows up under load. Want a quick read on your own app first? Run the free scan on your live URL.

QuestionAnswer
What breaks first as traffic grows?
N+1 queries. One query becomes fifty, and nobody notices until real data arrives.
What's the single best fix to start with?
Add indexes on the columns you filter by most. Tenant ID and user ID first.
Do small apps need connection pooling?
Yes, sooner than you'd think. Serverless functions burn through connections fast.
Is this a security issue too?
Sometimes. Slow queries push teams toward shortcuts that skip access checks.
How do I check my own app?
Run the free scan, or run EXPLAIN ANALYZE on your slowest page's queries.

N+1 queries: the most common performance finding we see

An N+1 query is simple to describe and easy to miss. You load a list of 50 rows, then run one more query for each row to get its details. That's 51 queries where a single join would do the same job in one.

AI coding tools write this pattern often. It's the most direct way to get a feature working: fetch the list, then loop over it and fetch what each item needs. With ten rows of test data, this runs in a blink. With a real user's data, it turns one page load into fifty or a hundred round trips to the database.

Missing indexes: the fix AI tools skip by default

Every table gets a primary key index automatically. Nothing else is automatic. If your app filters or sorts by any other column, and there's no index on it, the database reads every row to answer that query.

For a multi-tenant app, the columns you filter by most are usually tenant ID, user ID, and created date. If those don't have an index, every query scoped to one tenant scans the whole table to find that tenant's rows.

Indexes and row-level security are linked too, more than most founders expect. If your RLS rule calls a function like auth.uid() the wrong way, the database can run that function once per row. It should only run once per query. This is a common mistake, and it is easy to fix. We cover it in our guide to Row Level Security performance.

Connection pooling: the outage you don't see coming

Every database connection has a cost. Every managed database caps how many it allows at once. Serverless functions make this worse. Each one can open its own connection. Add a traffic spike, and hundreds of connections can open in seconds.

Without pooling, one of two things happens under load. New connections get refused. Or the database runs out of memory holding them open. Either way, real users see errors that have nothing to do with your code.

Transaction boundaries: stop partial failures from sticking

A transaction that wraps too much work is slow. It holds locks longer than it needs to. A transaction that wraps too little work is risky. It can leave you with half-finished writes when something fails partway through.

SymptomUsual causeFirst thing to check
List or dashboard page slows down as data growsN+1 queriesCount queries per request on that page
One query is fine alone, slow at scaleMissing indexEXPLAIN ANALYZE the query
Random "too many connections" errors under loadNo connection poolingCheck your provider's connection limit vs. peak usage
Partial or duplicate records after a failureTransaction boundaries too wide or missingTrace what happens if the request fails halfway through

These four patterns sit in the Performance dimension of every scan we run. Security, Reliability, and Monitoring are the other three. Need the security side of database work too? Row-level security, storage buckets, backups? That is a separate guide: our database hardening checklist.

Find out which of these your app has

Free URL-based scan. No code access. About 30 seconds. Security, reliability, performance, and monitoring in one score.

Run the free scan

What a full audit adds on top of the free scan

The free scan is $0. It gives you a Launch Readiness Score. The full Launch Readiness Audit is $499 one time. A senior security engineer reviews every finding. You get file references where they apply, plus a fix roadmap. Delivered within 48 hours. Ongoing monitoring starts at $149 a month.

FAQ

What is an N+1 query and why does AI-generated code create them?

An N+1 query is one query to get a list, then one more query for each row in that list. Load 50 users, then run 50 more queries to get each user's data. That is 51 queries where a single join would do. AI tools write this pattern often, because it is the simplest way to get a feature working, and it never gets flagged until real traffic hits it.

How do I find N+1 queries in my own app?

Turn on query logging for one busy page and count the queries per request. A details page or dashboard that fires ten or more near-identical queries is almost always an N+1 pattern. Most ORMs also have a built-in query counter for local development.

Do I need connection pooling for a small SaaS app?

Yes, well before you expect to. Every serverless function or background job that opens its own database connection adds up fast, and most managed databases cap total connections. Without pooling, a burst of traffic or a stuck background job can use up every connection and lock out real users. Most managed Postgres providers, including Supabase, ship a pooler you can turn on.

How do I know which columns need an index?

Start with any column you filter or sort by on a page that loads often, and any column used to scope one tenant's rows away from another's. Run EXPLAIN ANALYZE on that query. A sequential scan on a large table is your signal to add an index.

Is a database performance check worth it if my app already passed a security scan?

Yes, they check different things. A security scan checks who can reach your data. A performance check tests whether your database survives real traffic. Both are part of the same free Launch Readiness Score, but N+1 queries and missing indexes will not show up as security findings.

Research sources