ULID vs UUID vs Nano ID: Choosing the Right Identifier
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.
Decode UUID and ULID strings to read version bits, variants, and timestamps — a practical debugging guide for APIs, logs, and database keys.
By codefunc
A UUID or ULID in a log line looks opaque until you need to know when it was created, which version it is, or whether a client invented a malformed ID. Guessing from string length alone is how teams mis-attribute bugs to "the database" when the real issue is a v4 key treated like a sortable v7, or a ULID pasted into a UUID column. Inspection turns identifiers into structured facts.
Bottom line: Decode before you assume. Use an ID inspector to read UUID version/variant and ULID timestamps in the browser. Generate test IDs with UUID and ID generator tools — keep production tokens and customer IDs on your machine, not in upload-based decoders.
Backend and frontend developers who debug primary keys, correlation IDs, webhook payloads, and support tickets that only include an opaque ID. If you choose ID formats in greenfield systems, pair this with a comparison of UUID vs ULID vs Nano ID; this article focuses on reading what you already have.
| Format | What you can extract |
|---|---|
| UUID | Version (1–8), variant, optional timestamp/node (v1/v6/v7), randomness layout |
| ULID | 48-bit millisecond timestamp, 80-bit randomness, lexicographic time order |
| Invalid | Wrong length, bad alphabet, misplaced hyphens, non-hex characters |
Paste a candidate string into the UUID & ULID Inspector to see structured fields instead of eyeballing hex.
Canonical text form:
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
^ ^
version variant
M).N). RFC 4122 / RFC 9562 variant 10xx is the common "RFC" variant.| Version | Meaning | Inspector focus |
|---|---|---|
| v1 | Time + node | Timestamp and (historically) MAC-derived node |
| v4 | Random | Almost no structure; confirm version nibble is 4 |
| v5 | Name-based SHA-1 | Deterministic from namespace + name |
| v6 | Reordered time | Time-sortable layout, modern alternative to v1 |
| v7 | Unix ms + random | Embedded Unix timestamp; great for DB keys |
| v8 | Custom | Layout is application-defined — inspector shows version only |
If support asks "when was this created?" and you have a v4 UUID, the honest answer is: you cannot know from the ID alone. If you have v7 or a ULID, you can.
Some libraries emit non-RFC variants or strip hyphens inconsistently. An inspector that reports variant helps explain interoperability failures between systems that validate strictly and ones that accept any 128-bit hex blob.
A ULID is 26 Crockford Base32 characters:
01ARZ3NDEKTSV4RRFFQ69G5FAV
└── timestamp ─┘└─ randomness ─┘
When logs show ULIDs, extract the timestamp to correlate with deploy windows, cron jobs, or outage timelines. The inspector converts that prefix to a human-readable instant so you do not hand-decode Base32 under pressure.
Symptoms: ORM rejects the value, Postgres uuid cast fails, or a client sends undefined stringified.
Checklist:
id_ prefix?Run the string through the ID inspector first. If it fails, stop treating it as a UUID in SQL.
Team expects inserts to be time-ordered; indexes fragment; ORDER BY id does not match created_at.
Inspect a sample of recent IDs:
Fix the generator, not the index — or add a real created_at and stop overloading the ID.
ULID and UUID v7 embed wall-clock time. A machine with a wrong clock emits IDs that sort into the future or past. If inspector timestamps disagree with server created_at by minutes, check NTP before blaming the database.
When a user pastes an ID into chat:
Inspection is only half the loop. For fixtures:
Prefer stable fixtures in unit tests (hard-coded IDs) and random generation in integration tests where collision policy matters. Document which version your production app emits so test data matches reality.
IDs are often considered public (they appear in URLs). Still:
codefunc inspectors and generators run client-side: paste, inspect, copy — with no upload of the string to codefunc servers. That matters when IDs appear next to customer payloads in the same clipboard buffer.
| Mistake | Why it hurts | Fix |
|---|---|---|
| Storing ULID in a UUID column | Type and validation errors | Use text/char(26) or convert deliberately |
| Assuming all UUIDs have timestamps | v4 has none | Inspect version first |
| Case-folding ULIDs incorrectly | Crockford allows some aliases | Normalize with a proper library |
| Comparing ID time to local TZ without conversion | Wrong incident window | Use UTC in logs and inspector output |
| Validating only with regex | Accepts wrong versions | Parse version/variant bits |
Use these codefunc tools alongside this guide:
Do not guess ID semantics from length alone. Inspect UUID version and variant, and decode ULID or UUID v7 timestamps when you need time correlation. Align generators with database ordering expectations, watch clock skew, and keep support workflows on structured decode rather than tribal knowledge. Use the ID inspector plus UUID and ID generator tools in the browser so identifiers never need to leave your machine for routine debugging.
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.
Turn OpenAPI operations into accurate curl commands, attach auth headers correctly, and use snippets to reproduce frontend API failures for backend teammates.
Get Content-Type right for APIs, file uploads, and static assets — charset parameters, multipart boundaries, sniffing risks, and a practical MIME lookup workflow.
Find a developer tool