A list page slows down as it grows. How do you prove the repeated-query problem N+1 is the cause?
You measure, not guess. Enable the query log — Laravel Debugbar, Telescope, or DB::listen() — and count queries per request: an N+1 shows as the same SELECT repeated with only the bound id changing, its count rising with the row count. Confirm the fix by re-measuring the query count, not the clock.
- ✗Reasoning from the code review alone instead of counting the queries an actual request fires
- ✗Looking in the slow-query log, where N fast queries never individually cross the threshold
- ✗Declaring victory on wall-clock time on a dev dataset too small for N to matter
- →How would you catch an
N+1in CI before it ships, rather than in production? - →Why is the query count a better regression signal here than the request's wall-clock time?
A guess is not a diagnosis. Turn on the query log and look at what the page actually sends to the database. An N+1 has a recognisable signature: the same SELECT with only the bound id changing, repeated exactly as many times as there are rows in the list.
SELECT * FROM posts LIMIT 25 -- 1
SELECT * FROM users WHERE id = 4 -- 0.4 ms -- +1
SELECT * FROM users WHERE id = 9 -- 0.4 ms -- +1
SELECT * FROM users WHERE id = 12 -- 0.3 ms -- +1
... (25 times)
Total: 26 queries, 11 ms
None of these lands in the slow-query log — each one is fast. It is the count that hurts. So the regression metric is queries per page, not time: on a dev dataset of 5 rows the clock shows nothing, while the counter already shows 6 instead of 2.
Tools: Laravel Debugbar and Telescope count queries per request; DB::listen() gives the same with no package; Model::preventLazyLoading() in the test environment turns lazy loading into an exception and catches the regression in CI. Verify the fix with the same counter: 26 → 2.