Nothing was expensive, it was all waiting
Seven purchase orders took two seconds to list. Three reads each, one after another, is twenty-one round trips. Read each thing once: seventy milliseconds.
Our speed board measures every screen from the browser that opened it, and for a long while the orders screen in the back office was its clearest finding. Three loads, averaging just over two seconds, with the server's own timing header saying two of those seconds were spent inside one route: the one that lists purchase orders. The way in was at its one-hop floor. The page made two calls, not twenty. Neither of the usual excuses applied. It was the route.
Before touching anything we timed it from a machine that sits beside the database, where a round trip costs about twenty milliseconds rather than the ninety a browser in the UK pays. Four runs: 416, 428, 467 and 455 milliseconds, for two and a half kilobytes describing seven orders. That number is the clue, if you know how to read it.
Three lines of code
Each purchase order is presented with its lines, its supplier and the venue it belongs to. The function that assembles one order did those three reads one after another, waiting for each. The list route then called that function one order at a time, waiting for each. Seven orders, three reads each, every one waiting for the last: twenty-one round trips in a line.
Twenty-one times twenty milliseconds is 420, which is what the machine beside the database measured. Twenty-one times ninety is 1.9 seconds, which is what the board measured from a real browser. Nothing in that route was expensive. No query was slow, no payload was heavy. It was all waiting, and the waiting scaled with the distance to the database.
We have hit this shape enough times now to have a name for it in the worklog. The hub screen was eighteen trips that became three. The dashboard collapsed to two. The same fault, in a route nobody had looked at because nobody had opened that screen with the board watching.
Collect the ids, read each thing once
The fix gathers every order's id first and then does three reads, all at once: one query for every line across all the orders, one for the distinct suppliers, one for the distinct venues. The single-order function now delegates to the many-order one with a list of one, rather than keeping its own copy of the logic. That matters more than it sounds. Eleven other callers present a single order, and if the two shapes could drift apart, a purchase order would look different on its own page from how it looks in the list.
The ids are chunked at fifty, because the list route has no limit and a query built from every order a company has ever raised would eventually pass the database's ceiling on bound parameters. A fix that falls over on exactly the tenant big enough to need it is not a fix.
Measured after, and the payload compared byte for byte
Same route, same seven orders, after the deploy: 78, 62, 59, 57 and 69 milliseconds. Roughly seven times faster in worker time. But the number that mattered more was a comparison. We saved the payload from production before the deploy and compared it with the one after. Identical. Three screens and a PDF read that payload, and a faster route that answers something subtly different is worse than a slow one that answers correctly.
The regression test rebuilds the old shape, serially, from the same seeded database and demands equality with the new one. That test passes before and after the fix by design, which is the point of a regression guard rather than a weakness. The others bite: with the batched reader stashed, four orders cost ten statements instead of four, and a separate test fails if the three reads run one after another rather than together, because fewer statements in a line would be the same bug in miniature.
What we have not claimed is the board's number. Four hundred and fifty milliseconds of worker time down to sixty should put the UK figure well under its two seconds, but that is arithmetic, not a measurement. The board will say when somebody next opens the screen, and we would rather report a measured number later than a predicted one now.