UUIDs are the closest thing computing has to a guaranteed-unique identifier. Generate one on your laptop, generate one in a data center on the other side of the world, and the odds of a collision are vanishingly small. They show up in databases, distributed systems, OAuth tokens, file paths — everywhere a sequential ID would create coordination problems. Here's the practical guide.
What Is a UUID?
A UUID (Universally Unique Identifier), also called a GUID (Globally Unique Identifier — same thing, Microsoft's name), is a 128-bit value usually written as 32 hexadecimal characters in five groups separated by hyphens:
550e8400-e29b-41d4-a716-446655440000
The format is defined by RFC 4122 (and updated by RFC 9562 in 2024). The 128 bits give you 2^128 possible values — roughly 3.4 × 10^38 — large enough that randomly generated UUIDs almost never collide.
The Different Versions
Not all UUIDs are random. The format reserves four bits to indicate the version, which tells you how the UUID was generated.
| Version | Source | Use Case |
|---|---|---|
| v1 | Timestamp + MAC address | Time-ordered, leaks the host's MAC |
| v3 | MD5 of namespace + name | Deterministic from inputs |
| v4 | Random / pseudorandom | General-purpose unique IDs |
| v5 | SHA-1 of namespace + name | Deterministic, more secure than v3 |
| v6 | Reordered v1 (sortable) | Time-ordered, no MAC leak |
| v7 | Unix ms timestamp + random | Time-ordered, database-friendly |
| v8 | Custom / vendor-defined | Reserved for application-specific schemes |
For most applications today, you want v4 (random) or v7 (time-ordered for database keys).
When to Use UUID v4
v4 is the default for a reason: it's purely random, requires no coordination, and reveals nothing about its origin.
f47ac10b-58cc-4372-a567-0e02b2c3d479
Use v4 when:
- You need a non-guessable token (session IDs, share links)
- You want to prevent ID enumeration attacks (don't expose
/users/1,/users/2) - You don't care about ordering
- You don't need to derive the ID from inputs
The downside of v4 in databases is insertion locality. Random UUIDs land in random index pages, causing index churn. For a high-write table, this can hurt performance noticeably compared to sequential IDs.
When to Use UUID v7
v7 is new (standardized in RFC 9562 in 2024) and solves the insertion locality problem. The first 48 bits are a Unix timestamp in milliseconds, followed by random bits:
018d3e5d-3a45-7000-8000-000000000000
└──── timestamp ────┘└── version + random ──┘
Two UUIDs generated a millisecond apart sort sequentially. They still feel random — adjacent IDs aren't guessable — but inserts cluster in the same index pages.
Use v7 when:
- The UUID will be a primary key on a high-write table
- You want to sort records by creation time without an extra timestamp column
- You're using PostgreSQL, MySQL, or any database where index locality matters
Most languages now have v7 support — uuidv7 in Node.js (uuid package v9+), uuid_v7 in Rust, Python's uuid7 (third-party), Go's github.com/google/uuid.
When to Use UUID v3 or v5
v3 and v5 are deterministic: the same inputs always produce the same UUID. They take a "namespace" UUID and a name string, then hash them.
v5(namespace=DNS, name="example.com") = cfbff0d1-9375-5685-968a-48ce8b50e3e0
Useful for:
- Deduplicating items where you can derive a stable input (e.g., a UUID per email address)
- Generating consistent IDs across distributed systems without coordination
- Migrating string keys to UUID columns deterministically
v5 (SHA-1) is preferred over v3 (MD5) — MD5 is broken for security purposes, though for non-cryptographic deduplication either works.
Why Not Just Use Auto-Increment?
The classic alternative is a sequential integer (1, 2, 3...). It's smaller (8 bytes vs 16 for a UUID) and indexes are denser. Trade-offs:
| Concern | Auto-Increment | UUID |
|---|---|---|
| Storage | 8 bytes | 16 bytes |
| Index efficiency | Best (sequential) | Poor (v4) / Good (v7) |
| Distributed generation | Requires coordination | None needed |
| Hides record count | Exposes (/users/8421) |
Hides |
| Mergeable across DBs | Conflicts | No conflicts |
| URL aesthetics | Short and clean | Long |
For internal IDs in a single-DB monolith, auto-increment is fine. For distributed systems, multi-tenant apps, public-facing identifiers, or anything you'll later need to merge from multiple sources, UUIDs win.
How to Generate UUIDs Online
Use DevZone's UUID Generator to generate one or many UUIDs of any version:
- Pick the version (v4 for general use, v7 for database keys).
- Choose how many to generate (1 to 1000).
- Copy the result, or download as a
.txtor.jsonfile.
The generation runs in your browser using the Web Crypto API — no UUIDs are ever sent to a server.
Generating in Code
JavaScript / Node.js (built-in):
import { randomUUID } from "crypto";
const id = randomUUID(); // v4
For v7, use the uuid package:
import { v7 as uuidv7 } from "uuid";
const id = uuidv7();
Python:
import uuid
id_v4 = str(uuid.uuid4())
# v7 needs the third-party `uuid7` package or Python 3.13+
Go:
import "github.com/google/uuid"
id := uuid.New() // v4
id7, _ := uuid.NewV7()
PostgreSQL:
-- v4 via pgcrypto extension
SELECT gen_random_uuid();
-- v7 via uuid-osp or custom function
Rust:
use uuid::Uuid;
let id_v4 = Uuid::new_v4();
let id_v7 = Uuid::now_v7();
Collision Probability
The number of v4 UUIDs you'd need to generate to reach a 50% chance of a collision is about 2.71 × 10^18. To put that in perspective:
- Generating one billion UUIDs per second
- Continuously for 100 years
- ...gives you about a 50% chance of one collision
In practice, treat v4 UUIDs as guaranteed unique. The bigger risk is buggy code that generates UUIDs from a weak random source — Math.random() in old JavaScript, random.random() seeded with a constant in Python tests, etc. Always use a cryptographic RNG.
FAQ
Are UUIDs case-sensitive?
No. Both 550e8400-e29b-41d4-a716-446655440000 and 550E8400-E29B-41D4-A716-446655440000 are the same UUID. Most libraries emit lowercase by convention. Microsoft tooling sometimes prefers uppercase.
What's a "nil" UUID?
00000000-0000-0000-0000-000000000000 — the all-zeros UUID, used as a placeholder meaning "no UUID assigned." It's officially defined in the spec, so you can match against it without ambiguity. There's also a "max" UUID (ffffffff-ffff-ffff-ffff-ffffffffffff) added in RFC 9562.
Can a UUID start with a number?
Yes — UUIDs are hex characters, so they can start with 0–9 or a–f. The version digit at position 14 (the start of the third group) is what tells you the version: 4 for v4, 7 for v7, etc.
Can I shorten a UUID?
You can re-encode the 128 bits in base64 (22 characters) or base58 (around 22 characters) to shorten the string while preserving the value. ULID and KSUID are alternative formats designed to be shorter and time-sortable from the start. They're not interoperable with UUIDs but solve similar problems.
Should I use UUID strings or binary in the database?
Postgres has a native uuid type that stores it as 16 bytes — use it. MySQL stores UUIDs as 36-character strings (CHAR(36) or BINARY(16) if you pack manually). Storing as a string costs 2.25× the space of binary and makes indexes larger. If your DB has a native UUID type, use it.