Backend & Data
The N+1 query problem
You write an innocent loop: fetch a list of orders, then for each order show the customer's name. It's clean, readable, and passes every test — because your test database has five orders. In production, that list has 500, and your one page just fired 501 separate database queries. This is the N+1 problem, and it's probably the most common performance bug in web apps.
Where the extra queries come from
One query gets the list of N rows. Then, as your code loops over them, each iteration triggers another query to fetch related data — N more. That's 1 + N. Each query is fast on its own, but every one is a round trip to the database: send, wait, receive. Do that 500 times in a row and the waiting dominates. It's death by a thousand fast queries.
Why it hides so well
It usually isn't code you wrote on purpose. An ORM (the library that turns objects into SQL for you) makes order.customer.name look like a harmless property access — but each one secretly runs a query. The convenience is the trap: the queries are invisible in the code, and invisible on small test data.
Don't ask the database 500 tiny questions in a loop. Ask it one big question that brings back everything you need at once.
The fix
- Fetch related data up front — a JOIN, or your ORM's "eager loading" / "include" — so it comes back with the list in one trip.
- Batch the follow-ups. If you must do a second query, load all the related rows in a single
WHERE id IN (…)instead of one per row. - Watch the query log in development. If one page fires dozens of near-identical queries, you've found an N+1.
It pairs with the other classic database lever: make sure the columns you're joining and filtering on are indexed. Fix the N+1 and add the index, and a page that took eight seconds often comes back in twenty milliseconds.