Pagination

Level: Intermediate 30–60 min

Concepts: Business LogicBoundariesValidationEdge Cases

Solutions: C# | TypeScript | Python


Build a pagination calculator that takes a total item count, page size, and current page — then returns everything a UI needs to render pagination controls.

Requirements

Given:

  • totalItems — total number of items in the dataset
  • pageSize — number of items per page
  • currentPage — the page being viewed (1-based)

Return a pagination result containing:

  • totalPages — total number of pages
  • currentPage — the current page (validated)
  • pageSize — items per page
  • startItem — the 1-based index of the first item on this page
  • endItem — the 1-based index of the last item on this page
  • hasPrevious — whether a previous page exists
  • hasNext — whether a next page exists

Validation Rules

  • totalItems must be ≥ 0
  • pageSize must be ≥ 1
  • If currentPage < 1, clamp to 1
  • If currentPage > totalPages, clamp to totalPages
  • If totalItems is 0, return a result with totalPages = 0 and no items

Test Cases

Total ItemsPage SizeCurrent PageTotal PagesStartEndPrevNext
10010110110falsetrue
100105104150truetrue
10010101091100truefalse
951010109195truefalse
1101111falsefalse
0101000falsefalse
10010010110falsetrue
10010991091100truefalse

Edge cases:

  • Exactly divisible (100 items / 10 per page = 10 pages)
  • Not divisible (95 items / 10 per page = 10 pages, last page has 5)
  • Single item
  • Zero items
  • Page number too low (clamp to 1)
  • Page number too high (clamp to last page)
  • Page size of 1 (every item is its own page)

Step 2: Page Window

Add a pageWindow(windowSize) method that returns the page numbers to display in the UI:

Current PageTotal PagesWindow SizePage Numbers
1105[1, 2, 3, 4, 5]
5105[3, 4, 5, 6, 7]
10105[6, 7, 8, 9, 10]
345[1, 2, 3, 4]
125[1, 2]

The window should be centered on the current page when possible, and shift when near the edges.

Bonus

  • Add offset-based calculation: return offset and limit for SQL queries
  • Add cursor-based pagination: given a sorted list and a cursor value, return the next/previous page
  • Add firstPage and lastPage helpers for quick navigation

Hint

Start with totalPages — it’s just ceiling division. Then startItem and endItem — simple arithmetic from page number and page size. Add clamping next. The page window is a separate concern — build it after the basic calculator is solid.

Reference Walkthrough

Reference implementations in C#, TypeScript, and Python live at tddbuddy-reference-katas/pagination. This is an F2 (light builder) kata: one primary entity (PageRequest), one small test-folder PageRequestBuilder (10–30 lines), and derived metadata (totalPages, startItem, endItem, hasPrevious, hasNext, pageWindow) computed up-front so callers never have to recheck clamping.

Scope note — PageRequest only. The reference covers the core calculator plus pageWindow for UI strips. The bonus features — SQL offset/limit helpers, cursor-based pagination, and firstPage/lastPage navigation sugar — are out of scope and listed as stretch goals in the repo README. Cursor-based pagination needs the sorted source list as a collaborator (F3 territory); the rest is one-line sugar over existing fields.

This kata ships in Agent Full-Bake mode at middle gear, the F2 (light builder) tier. See the repo’s Gears section for why middle gear is the deliberate choice.


  • Examples Pin Intent. Properties Pin the Invariants.
    Example-based tests anchor scenarios in the team's vocabulary. Property-based tests anchor invariants across the input space. The agent needs both axes, and most suites only carry one.
  • The Test Pyramid Was an Economic Argument
    The test pyramid was not a quality law. It was a cost structure: unit tests were cheap, integration tests were expensive, so you wrote many of the first and few of the second. Agents collapsed the cost of writing tests at every level, and the cheapest test that still tells the truth is the one that pins a seam the agent cannot fake.
  • Agents Amplify Whatever Vocabulary They Find
    Agents do not bring vocabulary to your codebase. They mirror what's already there. Strong vocabulary gets agent contributions that read like the team. Weak vocabulary gets faster mediocrity. The work you did for craft reasons just became leverage.