Every distributed system eventually needs to answer one question: how do I generate a unique identifier without asking a central database for permission? The classic answer is an auto-incrementing integer, but that requires coordination, reveals cardinality, and leaks information. A UUID — Universally Unique Identifier — solves all three problems at once.
What a UUID actually is
A UUID is a 128-bit identifier, typically written as a 36-character string in the format 8-4-4-4-12 — for example, 550e8400-e29b-41d4-a716-446655440000. The uniqueness doesn't come from checking against a central registry; it comes from the sheer size of the space and how the bits are generated. With 122 random bits of entropy in the most common version, the odds of a collision are astronomically small.
- v4 — fully random. The best default for almost everything.
- v1 — timestamp + node identifier. Rough chronological ordering, but leaks the generating machine.
- v7 — timestamp + randomness. Sortable like v1 without the privacy trade-off, and the modern recommendation.
Why version 7 is taking over
UUID v7 embeds a millisecond timestamp at the front, so records are naturally sortable — which plays perfectly with database indexes and event streams where v4's randomness causes index fragmentation.
Where UUIDs shine
- Database primary keys — no counter, no coordination, merge-friendly.
- API resource IDs — safe to expose, impossible to enumerate.
- Idempotency keys — let clients retry safely without duplicate side effects.
- Event and log correlation — generated independently across services.
The catch is that you should generate UUIDs using a cryptographically secure random source, not a predictable one. Math.random-based generators can repeat under load — a silent, intermittent bug that's miserable to debug.
Generate UUIDs the right way
ForgePlug's UUID Generator creates v1, v4, and v7 IDs using your browser's Web Crypto API, validates any UUID you paste, and exports batches up to 100 in TXT, CSV, or JSON.
Open UUID GeneratorHow unlikely is a collision, really
With 122 random bits, the relevant maths is the birthday bound rather than the raw size of the space. Reaching even a 50% chance of a single collision requires generating on the order of 2.3 quintillion UUIDs. At a rate of one billion per second, that is roughly 70 years of continuous generation. For any realistic application, v4 collisions are not a risk worth engineering around.
The caveat is that all of this assumes a genuinely random source. The failure mode in practice is never the mathematics — it is a generator seeded poorly, or one built on Math.random, which is not cryptographically secure and can produce repeats far sooner than the theory suggests. Worse, several processes starting simultaneously from a similar seed can generate overlapping sequences, which surfaces as rare duplicate-key errors under load that are extremely unpleasant to diagnose.
The database mistake that costs the most
Storing a UUID as text is the single most common and most expensive error. A UUID is 128 bits — 16 bytes. Written as a hyphenated string it is 36 characters, so a VARCHAR(36) column uses more than twice the storage, and every index entry and comparison carries that penalty too. Databases with a native UUID type (PostgreSQL) or a binary column (MySQL's BINARY(16)) store the value properly. On a large table the difference is substantial, and it is invisible until you look for it.
The second cost is index fragmentation, and this is the real reason v7 exists. Database indexes are B-trees, and inserting sequential values appends neatly to the end of the structure. Inserting random values scatters writes across the whole tree, causing page splits, poor cache locality, and — in engines with clustered primary keys such as InnoDB — physically reordering row data. Under sustained insert load this degrades write throughput noticeably. UUID v7's leading timestamp restores that sequential ordering while keeping the coordination-free generation, which is why it has become the recommended default for new systems.
A UUID is an identifier, not a secret
v4 is unguessable enough to prevent casual enumeration, but v1 and v7 both embed a timestamp — and v1 also embeds a machine identifier. Treat a v7 ID as publicly revealing roughly when the record was created. Never use any UUID as a password-reset token, session token, or access credential; those need a purpose-built secret with its own expiry and revocation.
When NOT to use a UUID
UUIDs are 16 bytes — four times larger than a 32-bit integer. On enormous tables with hundreds of millions of rows, that extra size costs storage and slows index scans, and the cost multiplies across every secondary index, since those typically carry a copy of the primary key. If you truly never merge data sets and never expose IDs, a plain integer is fine. For almost everything else, a UUID — especially v7 — is the safer default.
A common middle path is to keep both: an internal auto-incrementing integer as the primary key for join performance, and a UUID as the externally visible identifier used in URLs and APIs. You get compact internal indexes and non-enumerable public IDs, at the cost of one extra unique index and a little discipline about which one leaves the system.
