Architecture tool
Choose an ID from your requirements, not habit
There is no universally best identifier. Describe what the ID must do and get a recommendation with the constraints that could change the answer.
Recommendation
CUID2 (default 24 characters)
Best fit for compact, distributed public IDs
CUID2 is compact, lowercase, URL-friendly, and can be generated by disconnected clients or multiple application nodes without coordination. Keep the default length unless you have quantified a different collision budget.
Why this fits
- ✓Your writers do not need a round trip to reserve an ID.
- ✓The lowercase Base36 representation stays readable in routes and logs.
- ✓The default length provides a very large collision space without UUID punctuation.
Trade-offs to accept
- △CUID2 is not an IETF identifier standard and may lack a native database type.
- △It is intentionally not time-sortable; query an explicit created_at column.
Suggested database shape
id VARCHAR(24) PRIMARY KEY
created_at TIMESTAMPTZ NOT NULL DEFAULT now()Generation starting point
import { createId } from "@paralleldrive/cuid2";
createId();Before shipping
- 1Generate with the maintained @paralleldrive/cuid2 package.
- 2Keep 24 characters unless a documented collision analysis supports another length.
- 3Validate format at trust boundaries and enforce a unique or primary-key constraint.
- 4Treat IDs as public and enforce authorization separately.
How the recommendation is made
The advisor starts with the system boundary. A single primary database can coordinate a compact numeric key; offline clients and independent nodes cannot. It then separates public record identifiers from bearer secrets and checks whether ordering or standards compatibility is a hard requirement.
The result is deliberately conservative: it keeps timestamps in an explicit created_at column, keeps authorization separate from identifier opacity, and does not shorten random IDs without a collision analysis.