Why Unique Identifier Strategy Matters in Software Architecture
Every record in a database, every event in a log stream, every message in a queue, and every entity in a distributed system needs a unique identifier. The choice of identifier format has far-reaching implications for database performance, system security, horizontal scalability, and API design.
Auto-incrementing integers (1, 2, 3...) are the simplest approach but expose several problems at scale. They leak business intelligence (competitors can estimate your total user count or order volume by observing sequential IDs), they create single-point-of-failure bottlenecks in distributed databases (which node assigns the next number?), and they enable enumeration attacks where attackers systematically access resources by incrementing IDs.
Random identifiers like UUIDs solve the enumeration and distribution problems but introduce their own challenges: they are larger (128 bits vs 32/64 bits), they fragment B-tree database indexes when inserted randomly (degrading write performance), and their text representations are long and unwieldy in URLs and logs.
Modern identifier formats like UUID v7, ULID, and NanoID represent different tradeoffs in this design space. Understanding these tradeoffs enables engineers to select the optimal identifier strategy for each specific use case rather than defaulting to a single approach.
A UUID v4 has 122 random bits, providing 5.3 × 10^36 possible values. You would need to generate approximately 2.71 × 10^18 (2.71 quintillion) UUIDs before having a 50% probability of a single collision. In practical terms, collisions are impossible.
UUID Versions Explained: v1, v4, v5, and v7
The UUID (Universally Unique Identifier) standard defined in RFC 9562 specifies several versions, each using different generation strategies.
UUID v1 combines a 60-bit timestamp (100-nanosecond intervals since October 15, 1582) with the network MAC address of the generating machine. This produces time-sortable IDs but exposes hardware identifiers and approximate generation times, creating privacy concerns. UUID v1 is rarely recommended for new applications.
UUID v4 generates 122 bits of cryptographic randomness (using crypto.getRandomValues in browsers or crypto.randomUUID()). This is the most widely used version because it requires no coordination between generating nodes, exposes no metadata, and is trivially simple to implement. The tradeoff is that random UUIDs fragment B-tree indexes in databases like PostgreSQL, MySQL, and SQLite, degrading insert performance by 20-40% compared to sequential inserts.
UUID v5 generates deterministic, namespace-based identifiers by hashing a namespace UUID and a name string using SHA-1. Given the same namespace and name, UUID v5 always produces the same output. This is valuable for generating stable identifiers from natural keys (e.g., generating a consistent UUID from a username or email address).
UUID v7 (RFC 9562, 2024) is the newest version and the recommended choice for new database-backed applications. It combines a 48-bit Unix timestamp (millisecond precision) with 74 bits of randomness. The timestamp prefix makes UUIDs roughly chronologically sortable, preserving B-tree locality and maintaining near-sequential insert performance while retaining the distributed generation and unpredictability benefits of random IDs.
Format comparison of common identifier strategies.
// Format examples for each UUID version:
// UUID v4 (random): no timestamp, pure randomness
// 550e8400-e29b-41d4-a716-446655440000
// UUID v7 (timestamp + random): chronologically sortable
// 018f3b3c-8e1a-7b2c-9d4e-5f6a7b8c9d0e
// └── timestamp ──┘ └─── random ───┘
// NanoID (21 chars, URL-safe): compact alternative
// V1StGXR8_Z5jdHi6B-myT
// ULID (26 chars, Crockford Base32): sortable + compact
// 01ARZ3NDEKTSV4RRFFQ69G5FAVPractice the concepts from this article using ToolZeno's browser-based developer tools. No sign-up, no server tracking — 100% client-side.
Random String Generator
FreeGenerate cryptographically secure random strings in bulk with customizable sets, exclusions, separators, and URL-safety constraints.
UUID Generator
FreeInstantly generate single/bulk UUIDs (v1, v4, v7) and validate formats client-side.
Nano ID Generator
FreeGenerate cryptographically secure, URL-safe Nano IDs with custom length and alphabet presets.
NanoID, ULID, CUID: Modern UUID Alternatives
Several modern alternatives address specific UUID limitations while maintaining uniqueness guarantees.
NanoID generates compact, URL-safe identifiers using a customisable alphabet. The default configuration produces 21-character strings using A-Za-z0-9_- (64 characters), providing 126 bits of entropy — comparable to UUID v4's 122 bits but in a string that is 15 characters shorter. NanoID uses crypto.getRandomValues for cryptographic security and is available in every major programming language. Its compact size makes it ideal for URL slugs, short codes, and systems where storage or bandwidth is constrained.
ULID (Universally Unique Lexicographically Sortable Identifier) combines a 48-bit millisecond timestamp with 80 bits of randomness, encoded in Crockford's Base32 format. The result is a 26-character string that is chronologically sortable (preserving B-tree locality like UUID v7), case-insensitive, and avoids confusing characters (no I, L, O, or U). ULIDs are popular in event sourcing architectures and time-series databases.
CUID2 (Collision-resistant Unique Identifier, version 2) generates secure, compact identifiers with built-in collision resistance. It uses a combination of timestamp, counter, fingerprint, and randomness, producing identifiers that are horizontally scalable without coordination. CUID2 was designed specifically for web applications and is optimised for distributed systems.
The choice between these formats depends on your specific requirements: UUID v7 for database primary keys (best B-tree performance), NanoID for URL-facing identifiers (compact and URL-safe), ULID for event logs and time-series data (sortable and readable), and auto-increment for internal counters where ordering matters more than security.
- UUID v7 is the best choice for new database primary keys — sortable, random, and RFC-standardised.
- NanoID is ideal for URL slugs, API keys, and user-facing identifiers — compact and URL-safe.
- ULID is perfect for event sourcing and time-series data — sortable and case-insensitive.
- Never use auto-increment IDs for user-facing APIs — they enable enumeration attacks and leak business data.
- Always use cryptographic random sources (crypto.getRandomValues) rather than Math.random() for ID generation.
Generating Secure Identifiers in the Browser
Modern browsers provide the crypto.randomUUID() API that generates RFC-compliant UUID v4 identifiers using the operating system's cryptographic entropy pool. This API is available in all modern browsers (Chrome 92+, Firefox 95+, Safari 15.4+) and Node.js 19+.
For NanoID and custom-alphabet identifiers, the Web Crypto API's crypto.getRandomValues() method fills a typed array with cryptographic random values. This is the same entropy source used by server-side key generation and is considered suitable for cryptographic purposes. Math.random() should never be used for identifier generation because it uses a pseudo-random number generator (PRNG) that may produce predictable sequences.
Client-side ID generation is valuable for several scenarios: creating unique keys for React list rendering without server round-trips, generating idempotency keys for API requests, pre-assigning resource IDs before submitting creation requests (enabling optimistic UI updates), and creating temporary identifiers for drag-and-drop or clipboard operations.
ToolZeno's UUID Generator and NanoID Generator use crypto.getRandomValues() to produce identifiers entirely in your browser. The generated IDs never pass through any server infrastructure, ensuring they cannot be predicted, logged, or correlated with your identity by external systems.