Database performance · ORM traps
N+1 Query Problem
Loop over users, fetch each user's posts. Watch query count and elapsed time explode — then watch one IN-clause make it disappear.
The most common backend performance bug in the world hides in three lines of innocent-looking ORM code: fetch a list of users, then for each user, fetch their posts. In development with 5 fake users it runs in milliseconds. In production with 200 real users it issues 201 round-trips and ships a dashboard that takes 8 seconds to load. Run both patterns side-by-side; watch the gap grow linearly with users; reveal the antidote — one line in every major ORM.
Explore the activity to earn its passport stamp. Stamps record exploration, not mastery.
What’s happening under the hood
- ›The ORM convenience trap: `user.posts` looks like a property access but is a network call. The code reads like a list comprehension; the runtime behavior is a tight loop of round-trips. This is why N+1 survives every code review — it doesn't look like a database access at all.
- ›Production data is what surfaces this. With 5 seed-data users the latency is invisible; with 200 real users at 20 ms each it's a 4-second page. By the time the bug is felt, the schema, the API contract, and the UI are all already shipped — so the fix shows up as a frantic patch instead of a design choice.
- ›Every major ORM ships a one-line antidote: Django `prefetch_related` / `select_related`, Prisma `include`, Laravel `with`, ActiveRecord `includes`, SQLAlchemy `joinedload`, GraphQL `DataLoader`. They all do the same thing: collect the parent IDs, fire one `IN`-clause or `JOIN` query, distribute the results to the right parents. The hard part is noticing the bug exists.
Dig deeper
Phase 4 · Backend EngineeringThe concept you just explored is taught with full depth in the formal DURA curriculum.