UUIDs Explained: Versions and Best Practices
Everything you need to know about UUIDs. Learn the differences between UUID versions and when to use each.
UUIDs Explained: Versions, Use Cases, and Best Practices
Choose the right UUID version for your application.
What is a UUID?
A UUID (Universally Unique Identifier) is a 128-bit identifier designed to be unique across space and time without central coordination. Also known as GUID (Globally Unique Identifier) in Microsoft ecosystems.
550e8400-e29b-41d4-a716-446655440000
The format is 32 hexadecimal digits displayed in five groups separated by hyphens: 8-4-4-4-12.
Why UUIDs?
Traditional auto-incrementing IDs have problems:
- Require coordination - A central database must assign IDs
- Expose information - ID 1000 reveals ~1000 records exist
- Merge conflicts - Combining databases causes ID collisions
- Scaling limits - Single point of failure for ID generation
UUIDs solve these by generating IDs anywhere, anytime, with virtually zero collision risk.
UUID Versions
Not all UUIDs are created equal. There are five standard versions, each with different properties.
UUID v1 (Timestamp + MAC Address)
Structure: 60-bit timestamp + 14-bit sequence + 48-bit MAC address
| timestamp |ver| seq | MAC address |
f47ac10b-58cc-11e8-a5c3-0242ac130002
Characteristics:
- Time-ordered (somewhat sortable)
- Reveals creation time
- Reveals machine MAC address
- Low collision risk within same machine
When to use: Rarely. The MAC address leak is a privacy/security concern. Mostly seen in legacy systems.
When to avoid: Any system where revealing creation time or machine identity is problematic.
UUID v4 (Random)
Structure: 122 random bits + 6 version/variant bits
| random bits |
f47ac10b-58cc-4d7a-9d7a-0242ac130002
^
version 4 marker
Characteristics:
- Completely random
- No embedded information
- Not sortable
- 2^122 possible values (~5.3 × 10^36)
Collision probability: If you generate 1 billion UUIDs per second, it would take about 100 years to have a 50% chance of a single collision.
When to use:
- Most applications
- When you don't need time-ordering
- Security-sensitive contexts (no information leakage)
When to avoid:
- Database primary keys in high-write scenarios (fragmentation)
- When you need sortability
UUID v7 (Time-Ordered Random) - NEW STANDARD
Structure: 48-bit timestamp (ms) + 74 random bits + 6 version/variant bits
| timestamp (ms) |ver| random |
0186d5a5-9e44-7xxx-xxxx-xxxxxxxxxxxx
Characteristics:
- Time-ordered (sortable!)
- Random component prevents conflicts
- Millisecond precision timestamp
- Better database performance than v4
- No MAC address exposure
When to use:
- Database primary keys (sortable, index-friendly)
- Distributed systems (time-ordered, no coordination)
- New projects (modern, best of both worlds)
- Event sourcing (chronological ordering)
When to avoid:
- If you need nanosecond precision (use v1 or custom)
- Legacy systems expecting v4 format (rare issue)
Why v7 is better for databases:
With v4's random distribution, new records are inserted throughout the B-tree index, causing page splits and fragmentation. With v7's time-ordering, new records are appended to the end, similar to auto-increment behavior, but without coordination requirements.
Generate: UUID Generator (supports both v4 and v7)
UUID v3 and v5 (Name-Based)
Structure: Hash of namespace + name
v3: MD5 hash (128 bits, 122 usable)
v5: SHA-1 hash (160 bits truncated, 122 usable)
Characteristics:
- Deterministic - same input produces same UUID
- Requires namespace UUID + string name
- v5 (SHA-1) preferred over v3 (MD5)
Example:
// Same input always produces same UUID
uuidv5('hello', NAMESPACE_DNS); // Always: 9342d47a-1bab-5709-9ee0-c85352fd3e4a
uuidv5('hello', NAMESPACE_DNS); // Always: 9342d47a-1bab-5709-9ee0-c85352fd3e4a
When to use:
- Generating consistent IDs from names
- URL-based identifiers
- When you need idempotent ID generation
Standard namespaces:
6ba7b810-9dad-11d1-80b4-00c04fd430c8- DNS6ba7b811-9dad-11d1-80b4-00c04fd430c8- URL6ba7b812-9dad-11d1-80b4-00c04fd430c8- ISO OID6ba7b814-9dad-11d1-80b4-00c04fd430c8- X.500 DN
UUID Version Comparison
| Version | Method | Sortable | Info Leakage | DB Performance |
|---|---|---|---|---|
| v1 | Time + MAC | Partial | High (time, MAC) | Good |
| v3 | MD5 hash | No | None | Poor |
| v4 | Random | No | None | Poor |
| v5 | SHA-1 hash | No | None | Poor |
| v7 | Time + Random | Yes | Low (time only) | Excellent |
UUID vs Other ID Formats
Auto-Increment
1, 2, 3, 4, 5...
| Aspect | Auto-Increment | UUID |
|---|---|---|
| Size | 4-8 bytes | 16 bytes |
| Coordination | Required (DB) | None |
| Sortable | Yes | v7 only |
| Information leak | Count exposed | None (v4) or time (v7) |
| Merge-safe | No | Yes |
Choose auto-increment when: Single database, low scale, foreign key optimization matters.
Choose UUID when: Distributed systems, multiple databases, security/privacy concerns.
ULID (Universally Unique Lexicographically Sortable Identifier)
01ARZ3NDEKTSV4RRFFQ69G5FAV
| Aspect | ULID | UUID v7 |
|---|---|---|
| Length | 26 chars | 36 chars |
| Encoding | Base32 | Hexadecimal |
| Sortable | Yes | Yes |
| Timestamp | 48-bit ms | 48-bit ms |
| Randomness | 80 bits | 74 bits |
Choose ULID when: String length matters, case-insensitive contexts.
Choose UUID v7 when: Broader compatibility, standard format expected.
Snowflake IDs (Twitter)
1234567890123456789 (64-bit integer)
| Aspect | Snowflake | UUID |
|---|---|---|
| Size | 8 bytes | 16 bytes |
| Structure | Time + Machine + Seq | Varies by version |
| Sortable | Yes | v7 only |
| Coordination | Machine ID needed | None |
Choose Snowflake when: Integer IDs required, high throughput, controlled infrastructure.
Choose UUID when: No machine ID coordination, broader compatibility.
NanoID
V1StGXR8_Z5jdHi6B-myT
Custom-length random IDs with configurable alphabet.
| Aspect | NanoID | UUID |
|---|---|---|
| Length | Configurable | Fixed 36 |
| Alphabet | Configurable | Hex |
| Sortable | No | v7 only |
| Standard | No | Yes (RFC 4122) |
Choose NanoID when: URL-friendly IDs, custom length requirements.
Choose UUID when: Interoperability, standard compliance.
Best Practices
For Database Primary Keys
- Use UUID v7 for new projects - sortable, distributed-friendly, index-efficient
- Store as binary (
BINARY(16)notVARCHAR(36)) - saves space, faster comparisons - Consider column order - put UUID columns after frequently-queried columns in composite indexes
-- Good: Binary storage
CREATE TABLE users (
id BINARY(16) PRIMARY KEY,
email VARCHAR(255),
-- Convert for display: BIN_TO_UUID(id)
);
-- Avoid: String storage (wastes 20 bytes per row)
CREATE TABLE users (
id VARCHAR(36) PRIMARY KEY,
...
);
For APIs
- Use lowercase with hyphens:
550e8400-e29b-41d4-a716-446655440000 - Validate format on input - reject malformed UUIDs
- Consider case-insensitivity - treat
550E8400same as550e8400
For Security
- Never use v1 if MAC address exposure is a concern
- v4 reveals nothing - safe for user-facing IDs
- v7 reveals creation time - may be acceptable depending on context
- Don't trust client-generated UUIDs - validate and potentially regenerate server-side
For Performance
- Batch generation - generate multiple UUIDs at once if needed
- Use native implementations - crypto libraries, not manual string manipulation
- Avoid string comparisons - compare as bytes when possible
Common Mistakes
1. Using UUID for Everything
UUIDs add overhead. For internal lookup tables with fewer than 1000 rows, auto-increment is simpler and faster.
2. Storing as Strings
VARCHAR(36) uses 37+ bytes. BINARY(16) uses exactly 16 bytes. That's 2.3x the storage for no benefit.
3. Assuming Chronological Order with v4
UUID v4 is random. Don't use it for "latest first" queries. Use v7 or add a timestamp column.
4. Generating in Browser without Crypto
// Bad: Math.random() is predictable
function badUUID() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
const r = Math.random() * 16 | 0;
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
});
}
// Good: Use crypto API
crypto.randomUUID(); // Native, secure
5. Ignoring Index Fragmentation
Random v4 UUIDs cause B-tree page splits. For high-write tables, this degrades performance over time. Use v7 or ULID instead.
UUID in Different Languages
JavaScript
// Native (modern browsers/Node 19+)
crypto.randomUUID(); // v4
// uuid package
import { v4 as uuidv4, v7 as uuidv7 } from 'uuid';
uuidv4(); // Random
uuidv7(); // Time-ordered
Python
import uuid
uuid.uuid4() # Random
uuid.uuid1() # Time + MAC
# v7 (Python 3.13+ or uuid7 package)
import uuid7
uuid7.uuid7()
Go
import "github.com/google/uuid"
uuid.New() // v4
uuid.NewV7() // v7
Java
import java.util.UUID;
UUID.randomUUID(); // v4
// v7 (Java 21+ or library)
PostgreSQL
-- Extension required
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- Generate
SELECT uuid_generate_v4(); -- Random
SELECT gen_random_uuid(); -- v4 (built-in since v13)
Conclusion
For most new projects, use UUID v7. It combines the best properties: globally unique without coordination, time-sortable for database efficiency, and no MAC address leakage.
Use v4 when you need maximum randomness and don't care about sorting. Use name-based (v5) for deterministic generation from strings.
Avoid v1 in new projects (MAC exposure) and v3 (MD5 is deprecated).
Generate UUIDs Now
Need UUIDs for your project? Use our UUID Generator to:
- Generate v4 (random) or v7 (time-ordered) UUIDs
- Generate in bulk (up to 1000 at once)
- Validate existing UUIDs
- Copy with one click
Last updated: January 2026
Related Tools
Related Articles
Test Data Generation: Complete QA Guide
How to generate realistic test data for your applications. Covers mock data, boundary values, and SQL test data.
Token Count Guide: AI Tokenization Explained
Learn what tokens are, why token count matters for AI models like Claude and GPT, and how to optimize your prompts for better results and lower costs.
PERT Estimation: Statistical Project Confidence
Learn the PERT three-point estimation technique for calculating expected durations with confidence intervals. Perfect for external commitments.