UUID Generation: Choosing the Right Version
"Just use UUID" sounds simple until you discover there are 8 versions with very different characteristics. Picking the wrong one can tank your database performance or leak information. Here's how to choose the right UUID version for your use case.
The Three UUIDs You Need to Know
While UUID has 8 versions, only three matter for most developers: v1 (legacy), v4 (current standard), and v7 (the future). Here's how they compare:
| Version | Based On | Sortable? | Privacy |
|---|---|---|---|
| v1 | Timestamp + MAC address | Partially | ❌ Leaks device info |
| v4 | Random numbers | No | ✅ Fully random |
| v7 | Timestamp + random | ✅ Yes | ✅ No device info |
The Modern Recommendation
For new projects in 2025, use UUID v7 for database primary keys. It combines v4's randomness with v1's time-ordering, giving you the best of both worlds without privacy concerns.
Understanding Each Version
UUID v1: The Legacy Option
Generated from timestamp + your computer's MAC address. While this guarantees uniqueness, it also exposes when and where the UUID was created—a privacy concern for public-facing IDs.
550e8400-e29b-11d4-a716-446655440000↑ The "1" indicates version 1
UUID v4: The Current Standard
Completely random with 122 bits of randomness. No information about when or where it was created. This is what most libraries generate by default and is suitable for most use cases.
f47ac10b-58cc-4372-a567-0e02b2c3d479↑ The "4" indicates version 4 (random)
UUID v7: The Future
Combines Unix timestamp in the first 48 bits with 74 bits of randomness. This makes them sortable by creation time while maintaining uniqueness and privacy. Ideal for database primary keys.
018e9d1c-e4a1-7b5c-9abc-def012345678↑ The "7" indicates version 7 (time-ordered)
Practical Code Examples
Generating UUIDs in JavaScript
// UUID v4 - Native browser/Node.js support
const uuidv4 = crypto.randomUUID();
console.log(uuidv4); // "f47ac10b-58cc-4372-a567-0e02b2c3d479"
// UUID v7 - Using the 'uuid' package
import { v7 as uuidv7 } from 'uuid';
const uuidV7 = uuidv7();
console.log(uuidV7); // "018e9d1c-e4a1-7b5c-9abc-def012345678"
// Custom UUID v7 implementation (if no library)
function generateUUIDv7() {
const timestamp = Date.now();
const timestampHex = timestamp.toString(16).padStart(12, '0');
const randomBytes = new Uint8Array(10);
crypto.getRandomValues(randomBytes);
// Set version (7) and variant bits
randomBytes[0] = (randomBytes[0] & 0x0f) | 0x70;
randomBytes[2] = (randomBytes[2] & 0x3f) | 0x80;
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, 4)}-${randomHex.slice(4, 8)}-${randomHex.slice(8)}`;
}UUID in Database Schemas (PostgreSQL)
-- PostgreSQL has native UUID support
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- Table with UUID v4 primary key (random)
CREATE TABLE users_v4 (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
email VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
-- For UUID v7, use gen_random_uuid() or application-generated
-- Note: Native v7 support coming in future PostgreSQL versions
-- Validate UUID format
SELECT id FROM users_v4
WHERE id::text ~ '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$';Common UUID Mistakes
Using UUID v1 in Public URLs
UUID v1 contains your MAC address and timestamp. Attackers can determine when records were created and potentially identify your servers. Use v4 or v7 for public-facing identifiers.
Using UUID v4 for High-Write Tables
Random UUIDs cause B-tree index fragmentation because new records insert randomly across the index. For tables with millions of inserts, use UUID v7 or consider ULID for better write performance.
Storing UUIDs as Strings
A UUID stored as VARCHAR(36) uses 36 bytes. Native UUID types use 16 bytes—less than half the storage. Always use your database's native UUID type when available.
Trusting Client-Generated UUIDs
Never trust UUIDs from untrusted clients for security-critical operations. Attackers can guess or brute-force sequential patterns. Generate UUIDs server-side for sensitive records.
Frequently Asked Questions
Can UUID v4 ever generate duplicates?
Theoretically yes, but practically no. With 122 random bits, you'd need to generate 2.71×10^18 UUIDs before having a 50% chance of a collision. That's about 1 billion UUIDs per second for 85 years. For all practical purposes, UUID v4 is unique.
Why is UUID v7 better for databases than v4?
UUID v7 is time-ordered (timestamp in the first bits), so new records naturally sort after old ones. This maintains B-tree index efficiency because new inserts go at the end. UUID v4 is random, causing inserts across the entire index—leading to page splits and slower writes.
Should I use UUID or auto-increment integers for primary keys?
UUIDs are better for distributed systems, preventing collisions without coordination. Auto-increment is faster for single-database apps and uses less storage (8 bytes vs 16). For most modern applications, especially those that might scale or need offline support, UUID v7 is the best choice.
Wrapping Up
UUID version choice matters more than most developers realize. For new projects, UUID v7 is the best default—it's sortable, private, and database-friendly. Use v4 when you need pure randomness, and avoid v1 unless you're maintaining legacy systems.
Next steps: Generate different UUID versions with our tool to see how they look, then update your database schema to use the appropriate version for your use case.
Related Resources
Internal Links:
External References: