UUID Guide: Versions, Use Cases, and Best Practices for Developers
Everything developers need to know about UUIDs — the differences between v1, v4, v5, and v7, when to use each version, database performance implications, and implementation in multiple languages.
What is a UUID?
A UUID (Universally Unique Identifier) is a 128-bit identifier that is designed to be unique across space and time without requiring a central authority. UUIDs are formatted as 32 hexadecimal digits displayed in five groups separated by hyphens:
550e8400-e29b-41d4-a716-446655440000
The format is: xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
Where M indicates the UUID version and N indicates the variant.
UUID Versions
UUID v1 — Time-based
Generated from the current timestamp and the MAC address of the computer. This means:
- Pros: Naturally sortable by creation time, guaranteed unique per machine
- Cons: Exposes the MAC address (privacy concern), requires clock synchronization
- Use when: You need time-ordering and don't care about privacy
UUID v3 — Name-based (MD5)
Generated by hashing a namespace UUID and a name using MD5:
// Same input always produces the same UUID
uuidv3("hello", uuidv3.URL);
// Always returns the same UUID for "hello" in the URL namespace
- Pros: Deterministic — same input always gives same output
- Cons: MD5 is cryptographically broken (though fine for UUID generation)
- Use when: You need reproducible IDs from known inputs
UUID v4 — Random
Generated from random (or pseudo-random) numbers. This is the most commonly used version:
f47ac10b-58cc-4372-a567-0e02b2c3d479
- Pros: Simple, no external dependencies, very low collision probability
- Cons: Not sortable, poor database index performance (random distribution)
- Use when: You need a simple unique identifier and don't need ordering
The probability of a collision with UUID v4 is astronomically low. You would need to generate about 2.71 quintillion UUIDs to have a 50% chance of a single collision.
UUID v5 — Name-based (SHA-1)
Same concept as v3 but uses SHA-1 instead of MD5:
- Pros: Deterministic, uses stronger hash than v3
- Cons: SHA-1 is also considered weak (but fine for UUID generation)
- Use when: Same as v3, but prefer v5 for new projects
UUID v7 — Time-ordered Random (New!)
The newest version, specified in RFC 9562 (2024). Combines a Unix timestamp with random data:
018f3e5c-8c1a-7000-8000-1234567890ab
^^^^^^^^ ^^^^
timestamp |
version 7
- Pros: Sortable by creation time, excellent database index performance, no privacy concerns
- Cons: Newer standard, less library support (growing rapidly)
- Use when: You need unique IDs in a database (this is the best choice for most new projects)
UUID v4 vs v7: Database Performance
This is a critical consideration. UUID v4's randomness causes index fragmentation in B-tree indexes:
UUID v4 inserts (random order):
f47ac10b... → page 847
a1b2c3d4... → page 123
e5f6a7b8... → page 756
13579bdf... → page 45
UUID v7 inserts (time-ordered):
018f3e5c... → page 999 (latest page)
018f3e5d... → page 999 (same page!)
018f3e5e... → page 999 (same page!)
018f3e5f... → page 1000 (next page)
UUID v7 inserts are sequential, which means:
- New rows go to the end of the index (no page splits)
- Better cache utilization
- 2-10x faster insert performance in benchmarks
- Smaller index size over time
Recommendation: Use UUID v7 for database primary keys in new projects.
Generating UUIDs
JavaScript
// UUID v4 (built-in)
crypto.randomUUID();
// "f47ac10b-58cc-4372-a567-0e02b2c3d479"
// UUID v7 (manual implementation)
function uuidv7() {
const timestamp = Date.now();
const timestampHex = timestamp.toString(16).padStart(12, "0");
const randomBytes = new Uint8Array(10);
crypto.getRandomValues(randomBytes);
const randomHex = Array.from(randomBytes)
.map(b => b.toString(16).padStart(2, "0"))
.join("");
return [
timestampHex.slice(0, 8),
timestampHex.slice(8) + randomHex.slice(0, 1),
"7" + randomHex.slice(1, 4),
((parseInt(randomHex.slice(4, 6), 16) & 0x3f) | 0x80)
.toString(16) + randomHex.slice(6, 8),
randomHex.slice(8, 20),
].join("-");
}
Python
import uuid
# UUID v4
print(uuid.uuid4())
# UUID v1
print(uuid.uuid1())
# UUID v5
print(uuid.uuid5(uuid.NAMESPACE_URL, "https://example.com"))
PostgreSQL
-- UUID v4 (built-in)
SELECT gen_random_uuid();
-- UUID v7 (PostgreSQL 17+)
SELECT uuidv7();
-- Use as primary key
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL
);
Command Line
# Linux/macOS
uuidgen
# Using Python
python -c "import uuid; print(uuid.uuid4())"
Best Practices
-
Use UUID v7 for database primary keys — Better performance than v4, no privacy issues like v1
-
Don't use UUIDs when you don't need them — Auto-incrementing integers are simpler and more efficient for internal-only IDs
-
Store as native UUID type — Most databases have a UUID type that stores 16 bytes. Don't store as VARCHAR(36) — that wastes space
-
Consider ULID as an alternative — ULIDs are similar to UUID v7 but use Crockford's Base32 encoding, making them shorter and URL-safe
-
Never assume UUIDs are secret — UUIDs are identifiers, not security tokens. Don't use them as authentication tokens or API keys
Generate UUIDs instantly with our UUID Generator tool.