Home / Blog / The N+1 Query Problem: Why Your App Gets Slower as Your Data Grows
EngineeringSeptember 10, 2026·3 min read

The N+1 Query Problem: Why Your App Gets Slower as Your Data Grows

What the N+1 query problem is, how to spot it in your logs, and practical ways to fix it with joins, eager loading, and batching.

Your app was fast in development. It was fast at launch. Six months later, the dashboard takes eight seconds to load and nobody changed that page. Before you blame the server or the framework, check for the most common performance bug in database-backed applications: the N+1 query.

What N+1 actually looks like

The pattern is simple. You fetch a list of records with one query, then loop over the results and fire another query for each one:

That's 51 round trips to the database for one page. With 10 rows in development, nobody notices. With 500 rows in production, the page crawls — and the slowdown scales linearly with your data, which is why it sneaks up on you months after launch.

ORMs make this easy to write by accident. Lazy loading means order.customer looks like a property access, but behind the scenes it's a fresh query. The code reads clean; the query log tells a different story.

How to spot it

You don't need fancy tooling. Two habits catch almost every case:

If a page runs more queries than it renders rows of unique data, something is looping where it should be joining.

The fixes, in order of preference

Use a join or eager loading. Every mainstream ORM has a way to say "load these orders and their customers in one go" — include, with, select_related, preload. This collapses 51 queries into one or two. It's usually a one-line change.

Batch by IDs. When eager loading doesn't fit — say, the related data lives in another service — collect the IDs first and fetch them in a single WHERE id IN (...) query or one bulk API call. Two round trips instead of N+1.

Denormalize deliberately. If a page always needs the customer's name next to the order, storing a copy of the name on the order row can be a reasonable trade. Do this consciously, document it, and accept the update cost — don't reach for it first.

Caching is not on this list as a fix. A cache in front of an N+1 pattern hides the problem until a cold cache or a cache miss storm brings it back at the worst possible moment. Fix the query shape, then cache if you still need to.

Keep it from coming back

N+1 bugs regress easily because the broken code still returns correct results. A teammate adds a field to a template, lazy loading kicks in, and the query count quietly triples. Defenses that work for small teams:

The takeaway: performance problems that grow with your data are design problems, not hardware problems. One afternoon reading your query log is worth more than a bigger database server.

Build with Abati Technology

We build software that ships — WhatsApp API, developer tools, POS, and mobile apps. Let's talk about your project.

Get in Touch →