A sequential case number is also a credential
The internal portal and public site of a regional public transport terminal, built end to end.
- Role
- Everything, from database to deploy
- Time
- 12 months
- Stack
- Next.js · Postgres · Prisma · Astro
- Team
- Solo
Problem
Requests with legal deadlines were tracked by hand, deadlines were counted in business days on a calendar, and reports were rebuilt in spreadsheets every month. The public site ran on an old WordPress.
Constraints
- Legal deadlines in business days, including holidays that move every year.
- One developer for the whole product, from database to deploy.
- The public site must never reveal anything about someone else's request.
- Case numbers are handed out under concurrent load.
Decisions
- Chose a Postgres advisory lock over SELECT … FOR UPDATE to hand out case numbers, because the race happens before the row exists; integration tests race real Postgres to prove it.
- Built the public lookup so a sequential case number exposes nothing on its own.
- Generated the holiday table (Easter-based and Monday-shifted holidays) instead of keeping a hand-written list, because a hand list breaks silently every year.
- Used one pure calculation engine for the screen and the PDF reports, so the two can never disagree.
- Retired the old site with redirects rebuilt from three generations of historical URLs, verified in production.
Outcome
The terminal runs its requests, deadlines, HR requests and reports on it, and the public site loads fast on a phone.
- Mobile LCP 9.8 s → 2.2 s
- Lighthouse 94 to 100
- About 1,300 unit tests