The Production Gap · Performance
100 orders. 101 database queries. Nobody notices until the page takes eight seconds.
The N+1 query problem is one of the most common performance bugs in web apps, and AI coding tools write it constantly. The code looks clean and the tests pass. Then real data arrives, and every list page gets slower with each new row.
The N+1 query problem happens when code runs one query to load a list, then one more query for every item in it: 100 items, 101 queries. AI tools write it because ORMs hide the extra queries inside clean-looking loops. The fix is eager loading: load the related data up front or in batches, so 101 queries become 2.
What is the N+1 query problem?
The N+1 query problem is a database performance bug where an app runs one query to fetch a list and then N more queries, one per item, to fetch related data. It works correctly but gets slower with every row, because the number of queries grows with your data.
With five rows in a test database, N+1 is invisible. With five thousand in production, every page load becomes thousands of database round trips.
Why do AI coding tools keep writing it?
Because the N+1 pattern looks like good code. Object-relational mappers (ORMs) like ActiveRecord, Django ORM, and Prisma let you write order.customer.name inside a loop, and quietly run a new query every time. AI assistants optimize for code that reads cleanly and passes tests, and tests run on tiny seed data where extra queries cost nothing.
Developers auditing AI-generated code keep finding it. One team that reviewed about 200 AI-written Entity Framework blocks described N+1 as queries disguised as clean code: a tidy loop where every property access triggers a lazy load.
It’s spreading beyond databases, too. One 2026 analysis showed how an AI agent looping over related records can end up making 10,000 database queries for what should be two.
How do you spot an N+1 query?
Look for pages that slow down as data grows, or logs full of nearly identical SELECT queries that differ only by ID. Monitoring tools like Sentry flag N+1 queries automatically, and frameworks have their own detectors, like the Bullet gem for Rails and django-debug-toolbar for Django.
How to fix the N+1 query problem
The fix is the same everywhere: eager loading. Tell the ORM which related data you need, so it loads it in one or two queries.
Ruby on Rails
N+1
orders = Order.all
orders.each { |o| o.customer.name }
Fix
orders = Order.includes(:customer)
includes loads every customer in one extra query.
Django
N+1
for order in Order.objects.all():
order.customer.name
Fix
Order.objects.select_related(
"customer"
)
Use prefetch_related for many-to-many and reverse relations.
Prisma
N+1
for (const o of orders) {
await prisma.customer.findUnique(
{ where: { id: o.customerId } })
}
Fix
prisma.order.findMany({
include: { customer: true }
})
include fetches the relation with the list instead of one by one.
GraphQL
N+1
// resolver runs once per order
customer: (order) =>
db.customer.find(order.customerId)
Fix
const loader =
new DataLoader(batchGet)
customer: (order) =>
loader.load(order.customerId)
DataLoader batches every lookup in a request into one query.
N+1 isn’t a bug you see. It’s a bill you pay with every new row.
Not sure how many N+1s your app has? Talk to a senior engineer →
How to keep N+1 queries from coming back
| Do this | Why it works |
|---|---|
| Turn on strict loading or a detector in development | Rails strict_loading, Bullet, or nplusone fail loudly on lazy loads before code is merged |
| Assert a query count in tests for key pages | A test that allows 3 queries breaks the day someone ships 101 |
| Seed realistic data, not five rows | N+1 only shows up when there’s enough data to multiply |
| Add a rule to your AI assistant’s instructions | “No queries inside loops; use eager loading” stops most N+1s at the source |
| Watch production with APM alerts | Catches the N+1s that slip through, before customers complain |
One caution: load only the relations the page actually shows, or you trade N+1 for one bloated query. For the other things that break as you grow, read Scalability testing for AI-built apps.
Find the N+1s hiding in your AI-built app.
Senior MagmaLabs engineers can trace your slowest pages, find the N+1s and other hidden performance bugs, and tell you plainly what to fix first.
Another bug AI tools keep writing: the multi-tenancy data leak →
Frequently asked questions
What is the N+1 query problem?
It’s a database performance bug where an app runs one query to fetch a list, then one additional query for each item to fetch related data. Loading 100 items takes 101 queries, and the count keeps growing with your data.
How do you fix the N+1 query problem?
Use eager loading: tell your ORM which related data to load with the list, so it runs one or two queries instead of one per row. In Rails that’s includes, in Django select_related or prefetch_related, in Prisma include, and in GraphQL a batching tool like DataLoader.
What is the N+1 query problem in Ruby on Rails?
In Rails, it happens when you loop over records and access an association, like order.customer, without preloading it. ActiveRecord then runs a query per record. Fix it with includes, and catch it early with strict_loading or the Bullet gem.
What is the N+1 problem in GraphQL?
In GraphQL, each field resolver can run its own query, so a list of 100 items with a related field triggers 100 extra lookups. DataLoader solves it by batching all lookups in a request into a single query.