0010 — Offset pagination, deliberately
Cursor pagination is better at scale and worse at everything a starting product needs.
Context
Cursor pagination is the technically superior answer and every engineer knows it. It is stable under insertion, it does not degrade at high offsets, and it is what you would choose for a feed with a million rows.
Decision
Offset pagination, chosen knowingly.
Cursor pagination is better at scale and worse at everything a starting product needs: jumpable page numbers, shareable URLs, a total count for "page 3 of 12".
A product with three hundred invoices needs "page 3 of 12" far more than it needs keyset stability. A product with three hundred thousand needs the opposite — and by then it has engineers who can make that change.
Consequences
The migration path is the reason this is defensible rather than merely pragmatic.
Paginated<T> is the only thing callers see, so the strategy can change without
touching call sites. The decision is reversible at a known cost, and that is
recorded here so the next person does not have to rediscover it.
nuqs carries the page in the URL (0011),
which is what makes "shareable URLs" true rather than aspirational.
When to revisit
When any of these becomes true, and not before:
- a list routinely exceeds a few thousand rows per tenant
count(*)shows up in slow query logs- rows are inserted fast enough that page 2 visibly skips or repeats items
None of these is a matter of taste, which is the point of writing them down: the trigger is measurable, so the argument does not have to be had again from first principles.
0009 — A calendar date is a date, not a timestamp
The best-evidenced decision in the codebase. Two shipped bugs, and both lq check and the generated tests passed throughout.
0011 — State management has a single answer
Server state is RSC and server actions. URL state is nuqs. Local state is useState. Everything else is a lint error.