Fix N+1 queries
Finds N+1 database queries behind an endpoint, page or job by counting real queries, fixes them with eager loading or batching, and adds a query-count test so they do not return.
An N+1 query happens when code loads a list with one query and then runs one more query per item, usually through lazy-loaded relations inside a loop or a serializer. It looks fine with test data and collapses with real data. The fix must be proven by counting queries, not by reading the code.
Find and fix N+1 queries in: Only if [QUERY_LOG] is given: Start from this log or trace:
- Identify the ORM or data layer and how to observe queries: enable query logging or use the framework's query counter or debug tooling.
- Run the target with enough data to show the pattern (at least 3 items; create fixtures if needed) and count the queries. Record the count and, if available, the time.
- Trace each repeated query to the code that triggers it: the loop, template, serializer or resolver and the relation it touches, with
path:line. - Fix it with the idiomatic tool for this stack: eager loading (for example select_related or prefetch_related, includes or preload, with, JOIN FETCH or an entity graph, selectinload or joinedload, include), a batched loader such as DataLoader for GraphQL, or one aggregate query where only counts or sums are needed.
- Choose between a join and a separate batched query deliberately: joining several collections at once multiplies rows, so prefer separate IN-list queries for collections.
- Re-run and count again. Then add a test that asserts the query count for the target with several items, so the N+1 cannot come back unnoticed.
- Load only the relations the code actually uses; do not over-fetch whole object graphs.
- Keep the response shape and ordering identical.
- Do not add caching as the fix for an N+1.
- Report real query counts from runs, not from reading the code.
- Before saying the work is done, run the check that proves it (tests, build, type check or the command the user gave) and report the real result.
- If you could not run a check, say so plainly and say which one.
- Do only what was asked. If you notice something else worth changing, mention it in one line at the end instead of changing it.
- Keep the change as small as it can be while still being correct.
Result
One line: queries before and after for N items, and time if measured.
Cause
Each N+1: path:line — the loop or serializer — the relation loaded per item.
Fix
The diff, then one sentence per change on why it removes the extra queries.
Regression guard
The test added and its result.
Other N+1 patterns spotted
Bullets with path:line, not fixed. Or "None".
1 required value still a placeholder; the assistant will ask for it.
details
- kind
- Prompt: a task you run by name to get one finished thing back
- domain
- Software engineering
- category
- Performance
- level
- Intermediate
- made for
- Backend engineer, Full-stack engineer, Software engineer
- needs
- repo-read, file-write, shell
- risk
- runs-commands
- version
- v1.0.0 · experimental
- reviewed
- 2026-10-02
- works in
- Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Antigravity, OpenCode, Windsurf, Zed, Continue, AGENTS.md
use in
npx @hermes-hq/hodios install fix-n-plus-one-queries --target claude-codenpx skills add hermes-hq/hodios-dist --skill fix-n-plus-one-queries -a claude-codeclaude plugin marketplace add hermes-hq/hodios-distclaude plugin install hodios-software-engineering@hodiosThe plugin brings every entry in this domain at once.
more in performance
All of PerformanceProfile and speed up a hot path
Measures a slow operation, profiles where the time goes, and makes it faster one verified change at a time, with before-and-after numbers. Use when an endpoint, command or function is too slow.
profile-hot-pathReduce JavaScript bundle size
Measures a web app's JavaScript bundles, finds the largest avoidable contributors, and shrinks them with verified changes ranked by bytes saved. Use when page load is slow or a size budget is blown.
reduce-bundle-sizeAnalyse load test results
Interprets k6, JMeter, Locust or Gatling results, finding the knee where latency climbs, separating load-generator limits from server saturation, and judging the pass criteria.
analyze-load-test-resultsFind a memory leak
Finds a memory leak from heap snapshots, memory metrics and code, naming the retaining path and the minimal fix with a regression check. Use when memory grows until a process is killed or restarted.
find-memory-leakFix slow or excessive React re-renders
Finds why React components re-render too often or render slowly, measures before changing anything, then fixes the cause with state changes or targeted memoisation. Use when a React UI feels laggy.
fix-react-rerendersHit a game frame budget
Diagnoses frame drops against a 16.6 or 33.3 ms budget from profiler captures, separating CPU from GPU bound, and ranks fixes by milliseconds saved per effort. Use before shipping on weak hardware.
hit-game-frame-budget