A list endpoint that works perfectly in every demo and every load test against a seeded thousand-row table can still corrupt what a real client sees the moment production traffic starts writing to that table while someone is paging through it. `?page=2&limit=20` looks simple and behaves simply — right up until a row is inserted or deleted between the client fetching page 1 and page 2, at which point the client silently sees a duplicate row, silently skips one entirely, or both, and nothing in the response tells them it happened. This isn't a rare race condition; on any table with real write traffic, a client paging through more than a screen or two of results will eventually hit it.
Pagination, filtering, sorting, and search are usually treated as one generic "list endpoint" problem, but they are three separable design decisions with genuinely different failure modes: how you page through results (offset, keyset, or an opaque cursor) determines whether the results stay stable while the underlying data changes; how you let clients filter and sort determines whether every query actually hits an index or silently forces a full table scan; and search is often not the same problem as either, because relevance ranking doesn't behave like a stable, resumable sequence at all.
Idempotency and cursor pagination are consistently where APIs get this wrong in production, not in a code review. A pagination design that looks identical to a correct one in a demo, and only reveals its instability under concurrent writes at real scale, is exactly the kind of defect that ships, works for months, and then produces a support ticket nobody can reproduce because the reporter can't say which specific row went missing.
Analogy🏏Cricket
🏏 Think of it like cricket: A Test match doesn't resume on day two by recounting deliveries from zero and hoping the ground staff remembered where things stood — the scoreboard picks up at the exact point play stopped: 267 for 4, the same two batters at the crease, the same over and ball, the same field settings noted in the umpires' record. Nobody says "we'll just start again from over fifty and count forward," because a full re-derivation from a fixed starting point, repeated every morning, would eventually drift from reality the moment anything about the previous day's record was slightly imprecise — a wrong over count, a substitution nobody logged. Instead, the match resumes from an exact, unambiguous bookmark: this specific score, at this specific point, for these specific players, and nothing about how many overs preceded it needs to be recomputed to know where to continue. Just as a Test match resumes from an exact bookmark rather than recounting from a fixed starting position, a client paging through a list of results should resume from an exact bookmark — a specific last-seen item — rather than a fixed numeric offset that has to be recounted from the top of the table every single request. Just as a wrong overnight record would corrupt day two's start, a data change between two page requests corrupts an offset-based "page 2" the moment anything shifted underneath that fixed number. The insight is that resuming from a specific, identifiable point in a sequence is fundamentally more robust than resuming from a position that has to be recounted from scratch every time, because the position can drift and the point cannot.
🏏 Showing the Cricket analogy — a Cricket version isn’t available for this concept yet.