What A UUID Is
A UUID (Universally Unique Identifier) is a 128-bit identifier designed to be globally unique in practice.
Most UUIDs are represented in the familiar 8-4-4-4-12 hexadecimal format:
550e8400-e29b-41d4-a716-446655440000
This string format is just a human-readable view of 16 raw bytes.
Why teams like UUIDs:
- IDs can be generated independently on many machines.
- No central counter is required.
- Very low collision probability for modern random versions.
UUID Versions You Should Know
Not all UUIDs are generated the same way.
v1: Timestamp + node info
UUID v1 uses time-based data plus node information (historically MAC-related identifiers). It is sortable by generation time, but can leak timing and machine hints.
v3: Namespace + MD5
UUID v3 is deterministic. Given the same namespace and name, it always returns the same UUID. Uses MD5 hashing.
v4: Random
UUID v4 is generated from random bits (with version/variant bits fixed). This is the most commonly used version in modern apps.
v5: Namespace + SHA-1
Like v3 but uses SHA-1. Also deterministic and often preferred over v3 in systems that avoid MD5.
Why v4 Is Usually The Default
Most product teams need an ID that is:
- Easy to generate anywhere
- Independent from centralized infrastructure
- Hard to guess sequentially
UUID v4 checks those boxes. It does not expose record creation order and works well across microservices, queues, client-generated drafts, and distributed uploads.
Collision Math: “1 In 2^122” In Plain English
A UUID has 128 bits, but version and variant consume some fixed bits. For v4, that leaves 122 random bits.
That means about 2^122 possible random outcomes.
This number is huge. In practical application scale, collision risk is negligible when using a strong randomness source.
You can still think probabilistically with the birthday bound, but for normal product volumes, collision handling is usually not the operational bottleneck. Bad randomness or incorrect implementation is a bigger risk than the math itself.
Real Use Cases
1. Database primary keys
Great when IDs are generated outside the database (clients, workers, services).
2. Session and request IDs
Useful for trace correlation and observability pipelines.
3. File uploads and object storage keys
Easy way to prevent filename clashes across distributed clients.
4. Event-driven systems
Reliable identifiers for messages flowing across independent producers.
UUID vs ULID vs NanoID
All three are useful, but for different priorities.
| Scheme | Strength | Tradeoff | Good fit |
|---|---|---|---|
| UUID v4 | Standardized, universal support | Not lexicographically time-sortable | General backend IDs |
| ULID | Sortable by timestamp, readable | Includes time component semantics | Ordered logs, event streams |
| NanoID | Shorter IDs, configurable alphabet | Not UUID standard | Frontend/public short IDs |
Quick picking rule:
- Need ecosystem compatibility across many systems? UUID v4.
- Need sort-friendly IDs with embedded time order? ULID.
- Need compact URLs or invite codes? NanoID.
Database Performance Notes (PostgreSQL vs MySQL)
UUID correctness is usually easy. Index behavior is the harder part.
PostgreSQL
uuid is a native type. It stores compactly and validates format. Random v4 inserts can cause less locality than sequential integers, which may increase index churn over time.
MySQL
Storing UUID as plain text (CHAR(36)) is simple but larger and slower than binary forms. Random inserts can also fragment clustered indexes depending on schema and engine behavior.
Practical guidance:
- Prefer native/binary UUID storage over long text when possible.
- Benchmark your real workload.
- If heavy write locality matters, consider ordered ID schemes.
Code Examples
JavaScript (Web / Node)
// Modern runtime with crypto.randomUUID
const id = crypto.randomUUID()
console.log(id)
Using a library for older environments:
import { v4 as uuidv4 } from 'uuid'
const id = uuidv4()
Python
import uuid
id_v4 = uuid.uuid4()
print(str(id_v4))
Go
package main
import (
"fmt"
"github.com/google/uuid"
)
func main() {
id := uuid.New()
fmt.Println(id.String())
}
SQL (PostgreSQL)
-- Extension setup depends on your environment
CREATE EXTENSION IF NOT EXISTS pgcrypto;
CREATE TABLE orders (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
total_cents integer NOT NULL
);
These examples all produce IDs that are straightforward to pass between services and clients.
Security And Operational Notes
UUIDs improve uniqueness, but they are not access control.
- Do not treat UUID opacity as authentication.
- Continue using authorization checks for every resource.
- Validate incoming UUID formats to avoid noisy logs and malformed queries.
For tamper-evident payloads, pair IDs with signatures or hashes as needed.
When You Should Not Use UUID As Primary Key
Consider alternatives when:
- You need strictly increasing, gap-sensitive numeric IDs.
- You have legacy integrations that require integers.
- Storage and index overhead must be minimized aggressively.
In those cases, incremental IDs or hybrid strategies (internal numeric ID + external UUID) can work well.
Bottom Line
UUIDs are popular because they solve real distributed-systems problems elegantly. UUID v4 is usually the safest default for general-purpose unique identifiers.
If you need ordered identifiers, evaluate ULID. If you need short public strings, evaluate NanoID.
Pick based on system requirements, not trend preference:
- Compatibility and standards: UUID v4.
- Time-order sorting: ULID.
- Short UX-facing IDs: NanoID.
A good ID scheme is mostly invisible when done right. It quietly prevents collisions, simplifies architecture, and scales with your system.