GUID v4 vs v7: Why You Should Care About the Shift
You are reviewing a pull request and see Guid.NewGuid(), uuid.uuid4(), or crypto.randomUUID(). It works, it compiles, and no one usually questions it.
That has been the default for years. GUID / UUID v4 is simple, widely supported, and effectively unique in real-world systems. So why change?
Because databases have evolved, distributed systems have evolved, and the standards have finally caught up. In 2024, RFC 9562 introduced GUID / UUID v7 as the time-ordered option, and major runtimes are already adding support.
This matters because v4 is not broken, but v7 solves problems v4 was never built to handle.
What Actually Changed: v4 vs v7
Both GUID / UUID v4 and v7 are 128-bit identifiers using the familiar 8-4-4-4-12 format. They look similar, but they are built differently.
v4 is almost entirely random. It gives you maximum unpredictability and no built-in ordering.
v7 puts a Unix timestamp in the first 48 bits, then adds randomness. Newer IDs sort naturally by creation time.
The visible difference is just the version digit: 4 versus 7. The storage format stays the same, and both are safe from collisions in practical systems.
Why v7 Matters in Databases
This is where v7 becomes more than a spec update.
Most relational databases use B-tree indexes for primary keys. With a random v4 GUID, inserts land all over the index. Over time, that leads to more page splits, more disk I/O, lower cache efficiency, and index fragmentation.
V7 behaves much more like an auto-increment key. Because new values cluster at the end of the index, inserts become more sequential. That usually means fewer page splits, better write performance, and less index bloat.
It also gives you chronological ordering for free. Sorting by the identifier often gives you a rough creation order, which is useful for pagination, audit trails, and event streams.
Language Support Is Already Arriving
RFC 9562 is not theoretical. The ecosystem is moving quickly, and v7 support is already landing in popular runtimes.
.NET
var v4 = Guid.NewGuid();
var v7 = Guid.CreateVersion7();
Python
import uuid
v4 = uuid.uuid4()
v7 = uuid.uuid7()
JavaScript / Node.js
const v4 = crypto.randomUUID();
import { v7 as uuidv7 } from 'uuid';
const v7 = uuidv7();
Go
v4 := uuid.New()
v7, _ := uuid.NewV7()
Rust
let v4 = Uuid::new_v4();
let v7 = Uuid::now_v7();
Java
UUID v4 = UUID.randomUUID();
// v7 still requires a library
The pattern is the same almost everywhere: a small code change, no schema change, and no central coordination.
v4 and v7 can also coexist in the same system, which makes migration much easier.
When v4 Is Still the Better Choice
Choosing v7 does not mean v4 is obsolete. There are still cases where v4 makes more sense.
Public identifiers and tokens. V7 exposes an approximate creation time, which may be undesirable for URLs, API keys, or session tokens.
Existing systems with no ordering needs. If v4 is already working well and ordering adds no value, there may be no reason to change.
Maximum randomness per ID. V4 gives more random bits overall, even if that rarely matters in practice.
Unreliable clocks. V7 depends on a reasonably accurate system clock, so some environments may be a poor fit.
In short, v4 is not deprecated. It is just no longer the only serious option.
A Simple Decision Rule
Here is a practical way to choose:
| Scenario | Recommendation |
|---|---|
| New database primary keys | v7 |
| Public-facing API keys or tokens | v4 |
| Existing v4 system with performance issues | Evaluate v7 |
| Existing stable v4 system | Stay with v4 |
| Event logs, audit trails, correlation IDs | v7 |
The goal is not to force v7 everywhere. The goal is to stop treating GUID version choice as an afterthought.
The Migration Is Simpler Than It Sounds
For most teams, migration is just a one-line swap at the point where IDs are generated.
| Language | v4 | v7 |
|---|---|---|
| .NET | Guid.NewGuid() |
Guid.CreateVersion7() |
| Python | uuid.uuid4() |
uuid.uuid7() |
| Node.js | uuidv4() |
uuidv7() |
| Go | uuid.New() |
uuid.NewV7() |
| Rust | Uuid::new_v4() |
Uuid::now_v7() |
No schema changes are required. Both versions fit the same column types, whether you use uuid, uniqueidentifier, or BINARY(16).
Use v7 for new tables and services.
Test it on high-write tables where index fragmentation matters.
Leave stable v4 systems alone unless there is a real reason to switch.
Make the Choice Deliberate
The industry is moving toward v7 as the new default, and for many systems that makes sense. It is faster for indexed inserts, easier to sort, and supported by the latest standards.
Still, the right decision depends on context. v7 is a strong default for databases, logs, and distributed systems. v4 is still the better fit for privacy-sensitive identifiers and systems that do not benefit from ordering.
The key is to choose deliberately.
If you enjoyed this post, follow me for more content like this!
Thanks for reading.
— Bruno

