UUID and GUID Explained: A Developer's Guide to v1, v4, and v7

You've seen them everywhere — in database primary keys, in API responses, in URLs, in log files. A 36-character string of hex digits and hyphens like 550e8400-e29b-41d4-a716-446655440000. They're called UUIDs (or GUIDs, depending on who you ask), and despite being everywhere, most developers couldn't tell you the difference between v1, v4, and v7 — or why it matters.

This guide explains how UUIDs work, what each version is for, the surprising performance trap that v4 has in databases, and why UUID v7 is the version you should probably be using in 2026.

What Is a UUID?

A UUID (Universally Unique Identifier) is a 128-bit number designed to be globally unique without needing a central authority. It's typically displayed as 32 hexadecimal characters split into five groups by hyphens:

550e8400-e29b-41d4-a716-446655440000
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
8        4    4    4    12      digits

M = version (1, 4, 7, etc.)
N = variant (almost always 8, 9, A, or B)

The "M" position encodes the version (which generation algorithm produced it), and the "N" position encodes the variant (how to interpret the rest of the bits). The other 122 bits carry the actual identifier data, which differs from version to version.

UUID vs GUID — Are They Different?

No. GUID (Globally Unique Identifier) is Microsoft's name for the same thing. Microsoft adopted UUIDs early and stuck with their own terminology in COM, .NET, and SQL Server. Outside the Windows ecosystem, the term is UUID. The bits are identical.

The only nuance: Microsoft's GUID display sometimes wraps the value in braces — {550e8400-e29b-41d4-a716-446655440000} — while everyone else omits them.

The UUID Versions That Matter

The UUID spec defines eight versions (v1 through v8), but only a handful are in real-world use:

VersionBased onSortableCommon use
v1MAC address + timestampPartialLegacy systems
v3MD5 hash of a nameNoDeterministic IDs
v4RandomNoMost common today
v5SHA-1 hash of a nameNoDeterministic IDs
v7Unix timestamp + randomYesNew default for DBs

UUID v4 — Random Bits

UUID v4 is the workhorse. 122 of its 128 bits come from a cryptographically secure random source, with the other 6 bits reserved for version and variant. Generated like this in most languages:

// JavaScript (Node 19+, modern browsers)
crypto.randomUUID()
// → "f47ac10b-58cc-4372-a567-0e02b2c3d479"

// Python
import uuid
uuid.uuid4()

// Go
import "github.com/google/uuid"
uuid.NewRandom()

// Swift
UUID()  // generates v4 by default

// Ruby
require 'securerandom'
SecureRandom.uuid

Collision Odds

Because v4 is essentially a 122-bit random number, collisions are astronomically unlikely. To have a 50% chance of producing a duplicate, you'd need to generate 2.71 × 1018 UUIDs — about 2.7 quintillion. If your application generated one billion UUIDs every second, you'd need to run for 86 years before reaching even a 1-in-a-billion collision risk.

For practical purposes, v4 collisions don't happen. You can treat them as unique forever.

The v4 performance trap: Random UUIDs as primary keys hurt write performance in B-tree-indexed databases (PostgreSQL, MySQL/InnoDB, SQL Server). Random inserts cause page splits and index fragmentation, which in heavy write workloads can cut throughput by 50% or more. This is the reason UUID v7 exists.

UUID v7 — Time-Ordered, Database-Friendly

UUID v7 was published in RFC 9562 (May 2024) specifically to fix the database problem with v4. Its layout:

0190d8c4-7e0e-7c39-9b1f-3a4b5c6d7e8f
└─── 48-bit ms ───┘ M    └── 74 random ──┘
   Unix timestamp     version

First 48 bits: Unix epoch in milliseconds (sortable!)
Next 4 bits:   Version "7"
Remaining:     Variant + 74 bits of randomness

Because the timestamp is at the front, v7 UUIDs sort chronologically when ordered as strings or bytes. Inserting them into a B-tree index appends to the end — the same write pattern as auto-incrementing integers, but with the global uniqueness of a UUID.

Generating UUID v7

// JavaScript (uuid 9.0+)
import { v7 as uuidv7 } from 'uuid';
uuidv7()
// → "0190d8c4-7e0e-7c39-9b1f-3a4b5c6d7e8f"

// Python (uuid-utils, or stdlib in Python 3.13+)
import uuid
uuid.uuid7()  // Python 3.13+

// PostgreSQL 18+
SELECT uuidv7();

// Go (google/uuid v1.6+)
uuid.NewV7()

Use v7 for new database tables. You get the operational benefits of UUIDs (no central counter, safe to generate offline, no exposing row counts) without paying the write-performance penalty of v4. If your DB or runtime supports v7, prefer it.

UUID v1 — The Original

UUID v1 was the first version, dating back to the 1980s. It combines:

Like v7, v1 is timestamp-based, but it's been superseded in modern systems for two reasons. First, embedding the MAC address leaks the originating machine's hardware identity — a privacy concern. Second, v1's timestamp byte order is shuffled in a way that defeats lexicographic sorting, so it doesn't actually give you the ordered-insert benefits that v7 does.

UUID v3 and v5 — Deterministic IDs

v3 and v5 are name-based: given the same namespace and name, they always produce the same UUID. v3 hashes with MD5; v5 hashes with SHA-1.

import uuid
NAMESPACE = uuid.NAMESPACE_URL
uuid.uuid5(NAMESPACE, "https://example.com/users/42")
# → always the same UUID for that URL

Useful when you want a stable identifier derived from a known input — for example, a content-addressable cache key, or a deterministic ID for a third-party resource you're mirroring.

ULID, NanoID, and Other Alternatives

If you don't need strict UUID compatibility, alternative ID schemes exist:

FormatLengthSortableNotes
UUID v436 chars (32 hex)NoUniversal support
UUID v736 charsYesNewer, time-ordered
ULID26 chars (Crockford Base32)YesPre-dates UUID v7; same idea
NanoID21 chars (URL-safe)NoShorter, URL-friendly
KSUID27 chars (Base62)Yes32-bit timestamp + 128-bit random
Snowflake64-bit integerYesTwitter; needs central coordination

For interop with the wider ecosystem — databases, UUID columns, JSON Web Tokens, OAuth scopes — stick with UUIDs. ULID/NanoID/KSUID are reasonable choices when you control the full stack and want shorter strings.

UUIDs in Databases

PostgreSQL

-- Native UUID type
CREATE TABLE users (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),  -- v4
    -- or, in Postgres 18+:
    id UUID PRIMARY KEY DEFAULT uuidv7(),
    name TEXT NOT NULL
);

MySQL / MariaDB

-- Store as BINARY(16), not CHAR(36) — saves 60% space
CREATE TABLE users (
    id BINARY(16) PRIMARY KEY,
    name VARCHAR(255) NOT NULL
);
INSERT INTO users VALUES (UUID_TO_BIN(UUID()), 'Paul');

SQLite

SQLite has no native UUID type. Store as TEXT (36 chars) or BLOB (16 bytes). For BLOB, you save space and gain faster comparison, at the cost of less convenient debugging.

When NOT to Use UUIDs

Quick Decision Guide

Inspect IDs and Tokens On Your Phone

BoltKit bundles 10 essential developer tools — JWTInspect for tokens, Base64Lab for encoding, JSONPretty for API responses, and more. Free on iPhone, iPad, and Mac.

Get BoltKit Free