If you've worked with UUIDs, you've almost certainly used UUID v4 — the random, hexadecimal string that looks like 550e8400-e29b-41d4-a716-446655440000. It's everywhere: database primary keys, API identifiers, session tokens.
But there's a newer format gaining traction: UUID v7. It solves a real problem with v4 that trips up developers at scale.
Here's what you need to know.
Quick refresher: What is a UUID?
A UUID (Universally Unique Identifier) is a 128-bit value used to identify something without a central authority. The format is always the same — 32 hexadecimal characters in 5 groups separated by hyphens: xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx.
The M position indicates the version. The N position indicates the variant.
UUID v4 — The random one
UUID v4 uses 122 bits of cryptographically random data. The remaining 6 bits encode the version (4) and variant (RFC 4122).
f47ac10b-58cc-4372-a567-0e02b2c3d479
^ (version 4 here)
Strengths:
- Simple — just generate 122 random bits
- Extremely low collision probability (2¹²² possible values)
- No information leakage — nothing encodes when or where it was generated
- Widely supported across every language and database
Weakness: they don't sort.
This sounds minor but causes real pain in databases. When you insert UUID v4 as a primary key, each new row lands at a random position in the B-tree index. This causes index fragmentation — the database constantly has to reorganise pages. At scale (millions of rows), this degrades write performance and increases storage overhead.
UUID v7 — The time-ordered one
UUID v7 was standardised in RFC 9562 (2024). The first 48 bits are a Unix timestamp in milliseconds, followed by 12 bits of sequence counter, then 62 bits of random data.
018f8e5b-b3c0-7c62-a98b-c8df7cdcbfe0
^^^^^^^^^^^^^^^^^ (millisecond timestamp here)
^ (version 7)
Because the timestamp comes first, UUID v7 values are lexicographically sortable by creation time. New rows always append to the end of the index. No fragmentation.
Strengths:
- Monotonically increasing — database indexes stay compact
- Embeds creation time — useful for debugging ("when was this record created?")
- Still globally unique — 62 bits of randomness per millisecond
- Compatible with systems expecting UUID format (same structure, just structured differently)
Weakness:
- Exposes approximate creation time — a privacy concern if IDs are public
- Requires a clock-aware generator — not just
random.bytes(16)
Which should you use?
Use v4 when:
- Privacy matters — the ID must not reveal creation time
- You don't control the database schema (third-party systems, legacy DBs)
- You're generating IDs in a context where order genuinely doesn't matter (session tokens, short-lived nonces)
Use v7 when:
- The ID is a database primary key — especially with B-tree indexes (PostgreSQL, MySQL, SQLite)
- You need sortable IDs without a separate
created_atcolumn - You're building event logs, audit trails, or append-heavy workloads
- You want timestamp debugging without adding a separate column
Rule of thumb: If you're in control of the schema and the data grows over time, v7 is the better default for primary keys. For everything else, v4 is fine.
Database benchmark reality
Here's the practical difference. In PostgreSQL, if you insert 10 million rows using UUID v4 as the primary key:
- Index pages are scattered across disk (random writes)
- Vacuum and autovacuum run more frequently
- Write-heavy workloads see 20–40% slower inserts compared to sequential IDs
With UUID v7, inserts are nearly as efficient as BIGSERIAL auto-increment, while still being globally unique and not requiring a central counter.
What about UUID v1?
UUID v1 also embeds a timestamp, but it has a critical flaw: it includes the MAC address of the generating machine. That's a privacy and security problem. UUID v7 solves this by using random bits instead of the MAC address for the non-timestamp portion.
UUID v1 is largely obsolete. Don't use it for new work.
What about ULID?
ULID (Universally Unique Lexicographically Sortable Identifier) predates UUID v7 and solves the same problem — sortable, time-ordered IDs. The difference is format: ULIDs use Crockford's Base32 encoding, so they're 26 characters instead of 36. They look like 01ARZ3NDEKTSV4RRFFQ69G5FAV.
If your team is already using ULIDs, stick with them. If you're starting fresh and want standard UUID format (widely supported by ORMs, databases, and tools), UUID v7 is the better choice now that it's RFC-standardised.
Generating UUIDs
Need to generate a UUID v4 for a project or test?
→ Free UUID Generator — snappytools.app generates UUID v4 values in bulk, right in your browser with zero setup.
For UUID v7 in code:
# Python — using the uuid7 package
pip install uuid7
import uuid7
print(uuid7.uuid7())
// Node.js — using uuid package (v9+)
import { v7 as uuidv7 } from 'uuid';
console.log(uuidv7());
-- PostgreSQL 17+ has native UUID v7 support
SELECT gen_random_uuid(); -- still v4
-- For v7: use pgcrypto or uuid-ossp extension, or generate in application layer
Summary
| UUID v4 | UUID v7 | |
|---|---|---|
| Sortable | ✗ | ✓ |
| Reveals creation time | ✗ | ✓ (approx) |
| DB index performance | Moderate | Excellent |
| Privacy-safe | ✓ | ✗ (exposes timestamp) |
| RFC standardised | ✓ (4122) | ✓ (9562, 2024) |
| Widely supported | ✓ | Growing |
UUID v4 is safe and simple. UUID v7 is better for database primary keys. Now you know the difference.
Top comments (0)