Standalone Mode
If invoked directly (not via the orchestrator), you must first:
- Read
../codeprobe/shared-preamble.md for the output contract, execution modes, and constraints.
- Load applicable reference files from
../codeprobe/references/ based on the project's tech stack.
- Default to
full mode unless the user specifies otherwise.
Performance & Scalability Auditor
Domain Scope
This sub-skill detects performance and scalability issues across these categories:
- N+1 Queries — Lazy-loading relationships inside loops
- Missing Indexes — WHERE/ORDER BY on non-indexed columns
- Unbounded Queries — Model::all() without pagination/limit
- Memory — Loading entire files into memory, array accumulation in loops
- Caching — Repeated identical queries, missing TTL, stale cache after writes
- Algorithmic Efficiency — O(n^2) in hot paths, nested loops, sorting in loops
- Concurrency — Race conditions, non-idempotent queue jobs, shared mutable state
- Frontend Performance — Unnecessary re-renders, bundle size, missing lazy loading
What It Does NOT Flag
- Premature optimization in non-hot-path code — proportional design matters. A utility function called once at startup doesn't need the same optimization as a request handler.
- Development-only debug queries — queries in seeders, dev-only commands, or debug endpoints.
- Batch processing scripts — scripts intentionally designed to process everything (migrations, data backfills) where unbounded queries may be appropriate.
- O(n^2) on small bounded collections (<100 items) — nested loops on small known-size arrays are fine.
- Frontend SSR/build-time code — server components and build scripts have different performance profiles than client-side code.
Detection Instructions
N+1 Queries
| ID Prefix |
What to Detect |
How to Detect |
Severity |
PERF |
Eloquent relationship access inside loop without eager loading |
Search for foreach/for loops iterating over a collection, then accessing a relationship property (e.g., $order->items, $user->profile) inside the loop body. Check whether the query that produced the collection includes with() or load() for that relationship. |
Critical |
PERF |
Any ORM lazy-loading inside iteration |
Look for patterns where a database query is implicitly triggered inside a loop: Django querysets accessed per-iteration, SQLAlchemy lazy loads, Prisma relation access in .map(). |
Critical |
PERF |
Template/view triggering queries |
Blade templates, Jinja2 templates, or React components calling relationship properties that trigger queries during rendering. |
Major |
Missing Indexes
| ID Prefix |
What to Detect |
How to Detect |
Severity |
PERF |
WHERE/ORDER BY on non-indexed columns |
Cross-reference query conditions (where(), orderBy(), WHERE, ORDER BY) with migration files or schema definitions to check for matching indexes. |
Major |
PERF |
Foreign keys without indexes |
Check migration files for foreignId(), foreign(), references() without corresponding index definitions. Most ORMs add these automatically, but raw migrations may miss them. |
Minor |
PERF |
Composite queries needing compound indexes |
Multiple where() conditions on different columns in the same query, or where() + orderBy() combinations that would benefit from a compound index. |
Minor |
Unbounded Queries
| ID Prefix |
What to Detect |
How to Detect |
Severity |
PERF |
Model::all() or SELECT * without limit |
Search for .all(), ::all(), findAll(), SELECT * FROM without LIMIT, paginate(), take(), or limit(). |
Major |
PERF |
Missing cursor/chunk for large dataset processing |
Operations that get() or load entire collections when processing large datasets. Should use cursor(), chunk(), lazy(), or equivalent streaming approach. |
Major |
Memory
| ID Prefix |
What to Detect |
How to Detect |
Severity |
PERF |
Loading entire files into memory |
file_get_contents() on user uploads or large files, fs.readFileSync() on variable-size files, Python open().read() without size limits. |
Major |
PERF |
Array accumulation in loops |
Arrays that grow inside loops without bounds or cleanup — collecting results in memory that could be streamed or yielded. |
Minor |
PERF |
Large collections instead of generators |
Returning full arrays/lists where generators (yield), lazy collections, or iterators would be more memory-efficient for large datasets. |
Minor |
Caching
| ID Prefix |
What to Detect |
How to Detect |
Severity |
PERF |
Repeated identical queries in same request |
Same query pattern executed multiple times within a single request/function call without caching the result. |
Minor |
PERF |
Cache without TTL |
Cache::put(), cache.set(), Redis SET without expiration. Data cached forever risks staleness. |
Minor |
PERF |
Cache not invalidated after writes |
Write operations (create/update/delete) that don't clear or update related cache entries. Stale cache served after mutation. |
Major |
Algorithmic Efficiency
| ID Prefix |
What to Detect |
How to Detect |
Severity |
PERF |
O(n^2) or worse in hot paths |
Nested loops iterating over the same or related collections. array_search/in_array/includes() inside loops (linear search in a loop = O(n^2)). |
Major |
PERF |
Sorting inside loops |
sort(), usort(), array_sort(), .sort() called inside a loop body. |
Major |
PERF |
Hashmap-replaceable linear search |
in_array(), .includes(), .indexOf(), list.index() used repeatedly on the same array where building a Set/dict/hashmap first would be O(1) per lookup. |
Minor |
Concurrency
| ID Prefix |
What to Detect |
How to Detect |
Severity |
PERF |
Race conditions in read-modify-write |
Patterns that read a value, modify it, and write back without locking: incrementing counters, updating balances, toggling flags in concurrent contexts. |
Critical |
PERF |
Queue jobs without idempotency |
Queue/job handlers that don't check for duplicate execution. Jobs that create resources without checking if already created. Missing unique constraints on job-created data. |
Major |
PERF |
Shared mutable state in async contexts |
Global/module-level mutable variables accessed in async handlers, request-scoped data stored in module scope. |
Major |
Frontend Performance
| ID Prefix |
What to Detect |
How to Detect |
Severity |
PERF |
Unnecessary re-renders |
Missing React.memo, useMemo, useCallback on expensive computations or components receiving new object/array references on every render. Objects/arrays created inline in JSX props. |
Minor |
PERF |
Large bundle imports |
import _ from 'lodash' (imports entire library), import moment from 'moment' (large library where date-fns or dayjs suffice), import * as icons from 'icon-library'. |
Minor |
PERF |
Missing lazy loading |
No React.lazy() / dynamic import() for route-level code splitting. Heavy components loaded eagerly on initial page load. |
Minor |
Optional Script Integration
When scripts/complexity_scorer.py output is available (run by the orchestrator during /codeprobe health or /codeprobe audit), use it to identify high-complexity functions as performance hot-spot candidates. Functions rated "high" or "very_high" that also appear in hot paths (request handlers, loop bodies, frequently-called utilities) are strong signals for algorithmic efficiency findings.
ID Prefix & Fix Prompt Examples
All findings use the PERF- prefix, numbered sequentially: PERF-001, PERF-002, etc.
Fix Prompt Examples
- "In
OrderController@index (line 22), add ->with('items', 'customer') to the Order::query() call to fix the N+1 problem — currently loading 2 relations lazily inside the Blade loop at orders/index.blade.php:15."
- "Replace
Product::all() at line 30 of CatalogService.php with Product::query()->paginate(25) or Product::cursor() if processing all records. The current query loads all products into memory."
- "In
ReportGenerator@aggregate (lines 45-60), the nested loop iterating $orders inside $customers is O(n*m). Build a lookup hashmap before the outer loop: $ordersByCustomerId = collect($orders)->groupBy('customer_id')."
- "Replace
import _ from 'lodash' at line 3 of src/utils/helpers.ts with specific imports: import debounce from 'lodash/debounce' to reduce bundle size."