Hunting N+1s and Killing Wasted Queries in Laravel
Performance work is often boring.
It is not always about hero stories where you cut latency by 80%. Most days, it is about cleaning up. You find queries you did not know you were running. Then, you delete them.
If your Laravel app is growing, you likely have these three problems.
- Hidden N+1 Queries
You cannot fix what you cannot see. Use a tool like beyondcode/laravel-query-detector in your local environment. It tells you when you use the loop-and-lazy-load pattern.
Do not run this in production. It adds overhead. Only register it when your app is in local or testing mode.
- Eager Loading Waste
People often fix N+1 problems by adding with(). But this creates a new problem. You might eager-load a relationship that your view no longer uses.
If a redesign removes a column from a table, you must remove the corresponding with() call in your controller. You are paying for data you throw away.
Treat every eager load as a claim. If you do not use the data, remove the claim.
- Recomputing the Same Data
Some values stay the same for the whole request. If you calculate them multiple times, you waste CPU. Use memoization to fix this.
Example: protected ?string $defaultConnection = null;
public function getDefaultConnectionName(): string { return $this->defaultConnection ??= $this->resolveDefaultConnection(); }
The ??= operator computes the value once and reuses it for the rest of the request.
- Dashboard Query Traps
Dashboards often run a separate count() query for every single card. If you have six cards, you run six queries.
Do these two things instead:
- Group your counts. Get all totals for one table in a single query.
- Use a short cache. Dashboard stats do not need to be live to the second. Cache them for five minutes.
One visitor pays the cost of the query. Everyone else gets the cached result.
Final Tip: Prevent Regressions
Cleanups fail if developers re-add the bad code later. Use Pest to assert your query counts. Set a ceiling for how many queries a page can run. If a developer adds an N+1, the test will fail.
Optimization is mostly about removal. Remove unused loads. Remove recomputed values. Remove unnecessary queries.
