Pagination
Level: Intermediate 30–60 minConcepts: 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 datasetpageSize— number of items per pagecurrentPage— the page being viewed (1-based)
Return a pagination result containing:
totalPages— total number of pagescurrentPage— the current page (validated)pageSize— items per pagestartItem— the 1-based index of the first item on this pageendItem— the 1-based index of the last item on this pagehasPrevious— whether a previous page existshasNext— whether a next page exists
Validation Rules
totalItemsmust be ≥ 0pageSizemust be ≥ 1- If
currentPage< 1, clamp to 1 - If
currentPage>totalPages, clamp tototalPages - If
totalItemsis 0, return a result withtotalPages = 0and no items
Test Cases
| Total Items | Page Size | Current Page | Total Pages | Start | End | Prev | Next |
|---|---|---|---|---|---|---|---|
| 100 | 10 | 1 | 10 | 1 | 10 | false | true |
| 100 | 10 | 5 | 10 | 41 | 50 | true | true |
| 100 | 10 | 10 | 10 | 91 | 100 | true | false |
| 95 | 10 | 10 | 10 | 91 | 95 | true | false |
| 1 | 10 | 1 | 1 | 1 | 1 | false | false |
| 0 | 10 | 1 | 0 | 0 | 0 | false | false |
| 100 | 10 | 0 | 10 | 1 | 10 | false | true |
| 100 | 10 | 99 | 10 | 91 | 100 | true | false |
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 Page | Total Pages | Window Size | Page Numbers |
|---|---|---|---|
| 1 | 10 | 5 | [1, 2, 3, 4, 5] |
| 5 | 10 | 5 | [3, 4, 5, 6, 7] |
| 10 | 10 | 5 | [6, 7, 8, 9, 10] |
| 3 | 4 | 5 | [1, 2, 3, 4] |
| 1 | 2 | 5 | [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
offsetandlimitfor SQL queries - Add cursor-based pagination: given a sorted list and a cursor value, return the next/previous page
- Add
firstPageandlastPagehelpers 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.
- C# (.NET 8, xUnit, FluentAssertions 6.12.0) — walkthrough
- TypeScript (Node 20, Vitest 1.6, TS 5 strict) — walkthrough
- Python (3.11, pytest) — walkthrough
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.
Related reading
- 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.