Inspecting UUIDs and ULIDs: Version, Variant, and Embedded Timestamps
Decode UUID and ULID strings to read version bits, variants, and timestamps — a practical debugging guide for APIs, logs, and database keys.
A practical comparison of UUID, ULID, and Nano ID — sortability, index performance, collision odds, and URL safety — so you can pick the right ID format for your database, API, and URLs.
By codefunc
Every system needs identifiers, and the default choice — a random UUID v4 — is not always the right one. The wrong ID format shows up later as fragmented database indexes, ugly URLs, or collisions you swore were impossible. UUID, ULID, and Nano ID solve the same problem with very different trade-offs. This guide compares them on the dimensions that actually affect production systems.
Bottom line: Use UUID v4 when you need a universal standard and do not care about ordering. Use ULID (or UUID v7) when IDs are primary keys and you want time-sortable inserts that keep your index healthy. Use Nano ID when you need short, URL-friendly IDs and control over length and alphabet.
| Property | UUID v4 | ULID | Nano ID (default) |
|---|---|---|---|
| Length | 36 chars (with dashes) | 26 chars | 21 chars |
| Alphabet | hex + dashes | Crockford Base32 | 64 URL-safe chars |
| Time-sortable | No | Yes (lexicographic) | No |
| Bits of randomness | 122 | 80 | ~126 |
| URL-safe | Needs no encoding | Yes | Yes |
| Standardized | RFC 9562 | Community spec | Community spec |
A UUID is a 128-bit value, usually rendered as xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx. Version 4 is almost entirely random:
f47ac10b-58cc-4372-a567-0e02b2c3d479
122 random bits make collisions astronomically unlikely. UUID v4 is the safe, boring, universally supported choice — every language and database understands it.
The catch is randomness kills locality. When a v4 UUID is a primary key, each insert lands at a random position in the index B-tree, causing page splits and cache churn. On write-heavy tables this measurably hurts throughput.
UUID v7 (part of RFC 9562) prefixes a millisecond timestamp before the random bits, making new IDs monotonically increasing:
018f a1b2 -7c3d -8e4f -a5b6 -c7d8e9f0a1b2
└─ time ──┘
If you want a standardized and sortable UUID, v7 is the modern answer and often a better default than v4 for database keys.
A ULID (Universally Unique Lexicographically Sortable Identifier) is 128 bits encoded as 26 Crockford Base32 characters:
01ARZ3NDEKTSV4RRFFQ69G5FAV
└── 48-bit time ─┘└─ 80-bit random ─┘
Because the timestamp is the most significant part, sorting ULIDs as strings sorts them chronologically. That gives you time-ordered inserts (index-friendly, like UUID v7) plus a shorter, case-insensitive, dash-free representation that reads cleanly in logs and URLs.
Crockford Base32 excludes ambiguous characters (I, L, O, U), which reduces transcription errors when a human has to read one aloud.
Nano ID takes a different angle: make the ID as short and URL-friendly as possible while staying collision-resistant. The default is 21 characters from a 64-symbol URL-safe alphabet:
V1StGXR8_Z5jdHi6B-myT
With 21 characters you get roughly 126 bits of entropy — more than UUID v4 — in fewer characters. You can shrink the length or swap the alphabet (for example, digits only, or a no-look-alike set) to trade entropy for brevity.
Nano IDs are not sortable and carry no timestamp. They are ideal for public-facing IDs: short links, invite codes, resource slugs in URLs.
The single most common reason teams switch away from UUID v4 is index fragmentation. A quick mental model:
| Key type | Insert location | Index health |
|---|---|---|
| Auto-increment int | Always at the end | Excellent |
| UUID v4 | Random | Poor on write-heavy tables |
| ULID / UUID v7 | Near the end (time-ordered) | Good |
If your IDs are primary keys on a busy table, prefer a time-sortable format (ULID or UUID v7). If IDs are secondary and you rarely sort by them, v4 is fine.
All three are safe at realistic scale, but the math differs:
Reducing Nano ID length lowers entropy fast — a 10-character Nano ID is fine for low-volume codes but not for high-cardinality keys. Use an entropy calculator before shortening below the default.
A predictable or enumerable ID that gates access is a vulnerability (insecure direct object reference). Two rules:
Use IDs to identify, use authorization to protect.
If your ID appears in URLs your users see or share, ULID or Nano ID reads better.
You can generate all of these locally — nothing leaves your machine — with the ULID & Nano ID generator and the UUID generator. For seeding a database with realistic records, pair them with the fake data generator.
// ULID and Nano ID in Node
import { ulid } from "ulid";
import { nanoid } from "nanoid";
const orderId = ulid(); // 01ARZ3NDEKTSV4RRFFQ69G5FAV
const shortRef = nanoid(10); // "V1StGXR8_Z"
Match the ID to the job. Time-sortable formats (ULID, UUID v7) keep database indexes healthy; Nano ID keeps URLs short; UUID v4 stays the universal fallback. Never use an identifier as a security token, and check entropy before trimming a Nano ID. Generate and inspect them locally before wiring them into your schema.
Decode UUID and ULID strings to read version bits, variants, and timestamps — a practical debugging guide for APIs, logs, and database keys.
Get Content-Type right for APIs, file uploads, and static assets — charset parameters, multipart boundaries, sniffing risks, and a practical MIME lookup workflow.
Turn OpenAPI operations into accurate curl commands, attach auth headers correctly, and use snippets to reproduce frontend API failures for backend teammates.
Find a developer tool