Comparing the common versions
| Version | Composition | Traits | Use for |
|---|---|---|---|
| v1 | Timestamp + node (MAC) | Roughly ordered but leaks time and device info | Internal legacy systems needing ordering |
| v4 | 122 random bits | Most common, unordered | General-purpose unique IDs |
| v7 | Timestamp prefix + random | Sortable by time, index-friendly | Database primary keys, log IDs |
How likely is a collision
For v4 you would need roughly 2.7×10¹⁸ UUIDs to reach a 50% chance of a single collision. At realistic scale, duplicates are not the problem — disorder is.
The primary-key performance trap
Random UUIDs scatter B+ tree inserts, causing page splits and write amplification; as data grows, insert throughput and cache hit rate both drop. Keep IDs time-local instead: use v7 / ULID, or an auto-increment or snowflake ID. Systems already full of v4 keys can keep the UUID as the public identifier and add an internal ordered key.
Three cases where UUIDs are the wrong tool
- Short, speakable identifiers: invite codes and short links need something shorter (a Base62 random string);
- Human-recognisable references: order numbers read better with semantic rules;
- Security tokens: see below.
Common questions
Can a UUID be a password or a token? No. A v4 UUID offers 122 random bits and carries no expiry, scope or revocation semantics; use a purpose-built token (256 random bits plus server-side validation). Does a UUID leak information? v1 leaks creation time and the device MAC; v7 leaks the creation time (usually acceptable); v4 leaks no structure at all.
Choosing an ID scheme
| Option | Ordering | Size | Use for |
|---|---|---|---|
| Auto-increment | Naturally ordered | Smallest | Single database, internal systems |
| Snowflake ID | Time-ordered | 64-bit integer | Distributed systems needing compact IDs |
| UUID v4 | Unordered | 36 characters | General unique IDs, public identifiers |
| UUID v7 / ULID | Time-ordered | 36 / 26 characters | Primary keys, log correlation |
Generating and using them well
- Use the platform or a standard library (
crypto.randomUUID(), language-specific uuid packages) — never build one fromMath.random(), which is not cryptographically secure; - Store 16 raw bytes or the 32-character hyphenless form: less space and faster comparisons than the canonical 36-character string;
- If you query by time, keep the ordering inside the ID (v7 / ULID); otherwise add an indexed created_at column;
- Assess leakage before exposing IDs: v1 carries time and MAC, v7 carries creation time, v4 carries no structure.
Real-world cases: three lessons from primary-key migrations
- "Writes got slower after switching to UUIDs": random v4 keys scatter B+ tree inserts and cause page splits. Fix: use v7/ULID, or keep an auto-increment key and expose the UUID only publicly.
- "Public IDs let people infer a pattern": v1 embeds time and MAC, potentially exposing creation time and device. Fix: expose v4 only, or at least never expose v1.
- "Logs can't be sorted by time because of the UUID": v4 is unordered. Fix: use v7/ULID for log IDs — time-ordered and easy to paginate.
FAQ
Can a UUID v4 collide? Extremely unlikely (about 2.7×10¹⁸ to reach a 50% chance), but add a unique constraint as a safety net. Can a UUID be a session token? Not advisable — it has no expiry, scope or revocation; use a 256-bit random string with server-side validation. Is dropping hyphens worth it? Smaller and faster to compare, slightly less readable; storage often uses 16 raw bytes. ULID or v7? Both are time-sortable; ULID is shorter (26 chars, Crockford Base32), v7 is standard UUID format (36 chars) — pick by ecosystem habit.
Try it: UUID generator
Version overview
- v1: timestamp plus MAC address — sortable but potentially leaking machine details;
- v3 / v5: deterministic hashes of a namespace and name (MD5 and SHA-1 respectively) — same input, same UUID, ideal as stable mapping keys;
- v4: 122 random bits, best privacy, but unordered and therefore random index writes;
- v6 / v7: newer standards putting time first for sortability; v7 aligns with Unix milliseconds and suits primary keys;
- Rule of thumb: v7/ULID for ordering, v5 for deterministic mapping, v4 when you only need uniqueness.
Migration and coexistence
Moving from auto-increment to UUID primary keys is expensive (index rebuilds, foreign key changes). The safer path is to keep the original key, add a public identifier column, backfill history, and only reconsider the physical key once every consumer has switched. Half-finished migrations leave data that does not reconcile.