UUID v4 Explained: Why You Need Them and How They Work
Why UUID v4 became the default identifier across modern systems, where it falls short, and when to reach for UUID v7 instead.
By Syed Husnain Haider Bukhari · · Updated
A UUID — Universally Unique Identifier — is a 128-bit number written as 32 hexadecimal characters in five groups: `550e8400-e29b-41d4-a716-446655440000`. The promise is that you can generate one on any machine in the world without coordinating with any other machine, and the probability of collision with someone else's UUID is so small it can be treated as zero.
That property has made UUIDs the default identifier in distributed systems, mobile apps, queue messages, log lines, and almost anything else that needs a stable name. This article explains how the magic works, why UUID v4 is so common, where it falls short in databases, and why UUID v7 is rapidly replacing it for new designs.
What "universally unique" actually means
A UUID v4 contains 122 bits of randomness (the remaining 6 bits encode version and variant). That's 2^122, or roughly 5×10^36 possible values. To get a 50% chance of collision by birthday paradox, you'd need to generate about 2.7×10^18 UUIDs. If a server generates a billion UUIDs per second, that's about 85 years of continuous work before a collision becomes likely.
In practice, that means UUID v4 collisions don't happen. Every popular language ships a generator that uses a cryptographic RNG; as long as the RNG isn't broken, your UUIDs are safe.
The UUID versions you'll actually see
RFC 4122 defines five versions; later RFCs add v6, v7, and v8. The ones that matter today: v1 uses a timestamp plus the machine's MAC address (privacy-leaking, mostly retired); v4 is fully random; v5 is a SHA-1 hash of a namespace plus a name, used for deterministic IDs (useful when the same input should always produce the same UUID); v7 is timestamp-prefixed random, designed specifically to be database-friendly.
v4 dominates. If you've ever called a `uuid()` function in your favorite language, you almost certainly got a v4. v7 is the new contender and is worth understanding before starting a greenfield project.
Why UUID v4 is great for distributed systems
Random UUIDs work beautifully in distributed systems because no two services need to coordinate to avoid collisions. A mobile app can mint an ID for a new draft offline; a server can mint an ID for a new user without checking with the database; a message queue can stamp an ID on every job without consulting a counter.
Compare that with auto-incrementing integer IDs, which require a single source of truth (usually the database) to allocate. In a multi-region deployment, distributed auto-increment requires consensus and adds latency. UUID v4 skips all that.
Why UUID v4 is bad for some databases
B-tree indexes — the standard index structure in PostgreSQL, MySQL/InnoDB, and SQLite — work efficiently when new keys are inserted in sequence at the end of the tree. Random UUIDs cause inserts to land anywhere in the tree, which fragments pages, blows up the working set, and tanks insert performance on large tables.
The effect is dramatic at scale. A table that does 50,000 inserts per second with sequential integer IDs may slow to 5,000 with random UUID v4 IDs because cache misses dominate. This is the single biggest argument against using UUID v4 as a primary key in high-write workloads.
UUID v7: random IDs that are also sortable
UUID v7 solves the database problem cleanly. The first 48 bits are a millisecond Unix timestamp; the remaining 80 bits are random. Two UUIDs generated in the same millisecond are still random with respect to each other, but UUIDs generated in different milliseconds are sortable by time. New IDs always land at the end of a B-tree index, restoring sequential insert performance.
Beyond performance, v7 gives you free benefits: IDs sort chronologically, so you can list records in creation order without an explicit timestamp column; debugging logs by ID becomes easier; you get a rough "when was this created?" from the ID itself. Every major language now has a v7 implementation. New projects should default to v7.
ULIDs, NanoIDs, and other alternatives
Before UUID v7 was standardized, several teams designed their own variants. ULID is identical in spirit to v7 (48-bit timestamp + 80-bit random) but uses Crockford base32 instead of hex, producing 26-character strings instead of 36. NanoID is fully random but shorter (21 characters at the default alphabet size).
If you're starting fresh today, prefer UUID v7 — it's standardized, well-supported, and indistinguishable from regular UUIDs to existing tooling. ULID and NanoID still have niches (URL-friendly shorter strings, smaller storage) but the standardization advantage of UUID v7 usually wins.
Practical tips
- Store UUIDs as native UUID type when your database supports it (PostgreSQL `uuid`, SQL Server `uniqueidentifier`). It's 16 bytes vs 36 bytes for the string form.
- Default to UUID v7 for new database primary keys; default to UUID v4 for distributed system message IDs and anything that should be unpredictable.
- Don't expose v1 UUIDs to end users — the embedded MAC address is a privacy leak.
- Use UUID v5 (namespace + name) when you need the same input to always yield the same ID — e.g. external system reconciliation.
- Don't try to guess UUID v4s for security purposes — they're unpredictable. But don't use them as auth tokens either; rotate to proper session tokens.
Wrapping up
UUIDs solved the coordination problem for distributed identifiers and made offline-first apps and multi-region services practical. UUID v4 is still the right choice for many use cases — anything that needs unpredictability or absolute decentralization. UUID v7 is the new default for database keys, mixing the coordination-free benefits of v4 with the index-friendly properties of sequential IDs.
If you need a UUID right now, our free UUID generator produces v1, v4, and v7 in any quantity, fully in-browser. Generate one or a thousand, copy them out, get back to building.