Complete Guide to UUIDs, GUIDs, and Unique Identifiers
UUID versions explained: v1, v3, v4, v5, v7
UUID v1 encodes a 60-bit timestamp and the MAC address of the generating machine. It is time-ordered but leaks hardware information and is rarely used in new systems. UUID v3 and v5 are name-based: given a namespace UUID and a name string, they deterministically produce the same UUID every time using MD5 (v3) or SHA-1 (v5) hashing. These are useful for generating consistent IDs from existing data. UUID v4 is the most common variant — 122 bits of cryptographically random data with no time or node information. UUID v7, defined in RFC 9562 (2024), encodes a 48-bit Unix millisecond timestamp in the high bits followed by random bits, combining time-ordering with randomness. For new systems, v4 or v7 are the only versions worth considering.
UUID v7: the time-ordered upgrade
The key advantage of UUID v7 over v4 is that it is lexicographically sortable by creation time. Because the first 48 bits encode the Unix timestamp in milliseconds, UUIDs generated in sequence compare as less-than in string order. This property is critical for database performance: B-tree indexes (used by PostgreSQL, MySQL, and most SQL databases) are most efficient when new records are inserted at the end, which happens naturally with time-ordered keys. UUID v4's random distribution causes frequent page splits throughout the B-tree, leading to index bloat and slower queries at scale. UUID v7 eliminates this problem while maintaining the same universally unique property.
NanoID vs UUID: choosing the right format
UUID is the standard choice for database primary keys, API resource identifiers, and any context where interoperability matters — every database, every language, and most frameworks have built-in UUID support. NanoID excels when you need a shorter, URL-safe identifier: at 21 characters it provides ~126 bits of entropy with a compact representation suitable for URLs, filenames, and tokens. NanoID has no standard format — its length and alphabet are customizable — which means there is no validation schema. If you paste a NanoID into a field expecting a UUID, it will be rejected. Use UUID for interoperability, NanoID for internal short IDs.
UUID as a primary key: performance considerations
Using UUID as a primary key in a relational database is a common pattern with real trade-offs. UUID v4 causes index fragmentation because each insert goes to a random location in the B-tree. At small scales this is unnoticeable; at millions of rows it causes measurable performance degradation. Solutions: (1) Use UUID v7 which inserts at the end like an auto-increment integer. (2) Use PostgreSQL's uuid_generate_v4() or gen_random_uuid() with a separate auto-increment surrogate key. (3) Use ULID (Universally Unique Lexicographically Sortable Identifier), which is similar to UUID v7. MongoDB uses a similar concept with ObjectIDs. (4) If using MySQL, store UUIDs as BINARY(16) not VARCHAR(36) — it uses less than half the space.
Generating UUIDs in code
JavaScript/Node.js: crypto.randomUUID() (built-in, v14.17+), or the uuid package: import { v4 as uuidv4 } from 'uuid'. Python: import uuid; str(uuid.uuid4()). Go: github.com/google/uuid — uuid.New().String(). Java: UUID.randomUUID().toString(). Rust: uuid crate — Uuid::new_v4().to_string(). PostgreSQL: gen_random_uuid() (v4) or pg_uuidv7 extension (v7). MySQL 8.0+: UUID() generates v1; UUID_TO_BIN(UUID(), 1) stores it efficiently. Command line (Linux/macOS): uuidgen. All modern languages have UUID v4 built in or via a widely-used library.
Validating UUIDs
A valid UUID matches the regular expression /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i. Breaking this down: 8 hex chars, hyphen, 4 hex chars, hyphen, version digit (1–8) + 3 hex chars, hyphen, variant bits (8, 9, a, or b) + 3 hex chars, hyphen, 12 hex chars. The version digit at position 14 tells you the UUID version. The variant nibble at position 19 is always 8, 9, a, or b for RFC 4122 UUIDs. Common validation mistakes: accepting version 0 (invalid), not checking the variant bits, accepting UUIDs without hyphens as-is without first inserting them.
Nil UUID, max UUID, and special values
The nil UUID (00000000-0000-0000-0000-000000000000) is defined in RFC 4122 as a special value meaning 'no UUID'. It is sometimes used as a sentinel value or placeholder. The max UUID (ffffffff-ffff-ffff-ffff-ffffffffffff) was formally defined in RFC 9562 and represents the largest possible UUID value. Both are valid UUID strings that pass format validation but are semantically special. Never use the nil UUID as a real identifier — it is frequently used as a default/zero value and will cause collisions if multiple records use it as a primary key.