Replace the Loop That Explodes With Real Data
Ten rows can hide almost any performance mistake. Ten thousand cannot. The classic version is a loop that scans another list for every item. At 10 by 10, that is 100 comparisons. At 10,000 by 10,000, it is 100 million.
Big O is a way to describe how work grows as input grows. You do not need the notation to find the bug. Count the repeated work.
Find the hidden multiplication
Look for code shaped like this:
const enriched = orders.map((order) => ({
...order,
customer: customers.find((customer) => customer.id === order.customerId),
}));
find starts at the beginning of customers for every order. Build an index once instead:
const customersById = new Map(customers.map((customer) => [customer.id, customer]));
const enriched = orders.map((order) => ({
...order,
customer: customersById.get(order.customerId),
}));
The first version repeatedly scans. The second pays once to build the map, then performs direct lookups.
Keep semantics before speed
A Map changes behaviour if duplicate keys exist. A Set removes duplicates. Sorting can change display order. Before replacing a data structure, write down:
- whether order matters;
- what duplicate IDs mean;
- what happens when a lookup is missing;
- whether keys are strings, numbers, or mixed;
- whether the collection changes after the index is built.
The faster answer is wrong if it silently chooses a different duplicate or stale value.
Benchmark the curve
Use the same deterministic fixtures at 10, 100, 1,000, and 10,000 records. Warm up the runtime, run each case several times, and record median plus a slow percentile. Do not compare two versions on different data.
The useful signal is not one timing. It is the shape. If doubling input makes runtime roughly four times worse, you likely have quadratic work somewhere in the measured path.
Also measure memory. The Map version stores an extra index, which is often the right trade, but it is not free.
What Claude can help with
Claude can inventory nested loops, repeated scans, repeated parsing, and array methods inside hot
paths. Ask it to explain what n represents and which behaviour must stay identical. Then make it
write correctness tests before the replacement.
Do not accept "this is O(n)" as evidence. The benchmark and call count are the evidence.
Run the starter locally
npm test
npm run validate
npm run sample
Dry-run validates the operation, data sizes, timings, and correctness-test inputs. Docker Compose runs the Node service and n8n without modifying your codebase.