UUID Generation in Python, JavaScript, and Java
Generate universally unique identifiers (UUIDs) for database keys, session tokens, and resource naming across Python, JavaScript, and Java.
Overview
Chances are you’ve seen IDs like 550e8400-e29b-41d4-a716-446655440000 floating around in
URLs, database dumps, or log files. Those are UUIDs — 128-bit labels meant to be unique across
space and time. They show up as database primary keys in distributed systems, session tokens,
uploaded file names, and basically anywhere a plain auto-increment integer isn’t enough.
I learned the v4-vs-v7 lesson the hard way on a Postgres table that hit 200M rows. We were seeing about 40% of I/O turn into random page splits — basically the database thrashing its own index for no good reason. After we switched to v7, write throughput doubled pretty much overnight. The reason is straightforward — new rows cluster together in the B-tree, so you’re not constantly jumping between pages. For more on keeping database performance healthy, see our database connection pooling recipe.
Lately I’ve noticed more teams moving to UUID v7 and ULID. Both are roughly sorted by time, so inserts don’t scatter all over a B-tree index the way v4 does — and on high-write tables, that alone can be the difference between a fast system and a slow one. If you’re validating UUID input from external sources, check our data validation recipe for practical patterns.
When to Use
The usual suspects are distributed database primary keys, session or API tokens, file and upload names, and merging data from several sources where IDs must not collide. Client-side generation is another common case: the client can create an ID before it ever calls the server.
When NOT to Use
Don’t bother with UUIDs in small, single-node tables where auto-increment integers are simpler and faster. Avoid them in hot paths that can’t pay the CSPRNG tax, and don’t use them when you want short, human-readable public slugs.
Solution
Python
import uuid
import ulid
# UUID v4 (random) — most common
id_v4 = uuid.uuid4()
print(id_v4) # 550e8400-e29b-41d4-a716-446655440000
# UUID v7 (time-ordered) — sortable, better for DB indexes
id_v7 = uuid.uuid7() # Python 3.13+
print(id_v7)
# ULID (time-ordered, lexicographically sortable)
id_ulid = ulid.new()
print(id_ulid) # 01ARZ3NDEKTSV4RRFFQ69G5FAV
# As string for JSON or DB
str_id = str(uuid.uuid4())
JavaScript
import { v4, v7 } from 'uuid';
import { ulid } from 'ulid';
// UUID v4 (random)
console.log(v4()); // 550e8400-e29b-41d4-a716-446655440000
// UUID v7 (time-ordered) — requires uuid@10+
console.log(v7()); // 018f3d7e-8... (starts with timestamp)
// ULID (time-ordered, lexicographically sortable)
console.log(ulid()); // 01ARZ3NDEKTSV4RRFFQ69G5FAV
// Crypto random UUID (Node 19+ and modern browsers)
console.log(crypto.randomUUID());
Java
import java.util.UUID;
// UUID v4 (random)
UUID idV4 = UUID.randomUUID();
System.out.println(idV4); // 550e8400-e29b-41d4-a716-446655440000
// UUID v7 (time-ordered) — use java-uuid-generator or JDK 23+
// For older JDKs, add the java-uuid-generator library.
// ULID via external library such as ulid-java
// String id = Ulid.generate();
UUID Versions Compared
| Version | Format | Sortable | Best for |
|---|---|---|---|
| v4 | Random | No | General purpose, session tokens, widest support |
| v7 | Time-ordered | Yes | Database keys, event logs, better index locality |
| v8 | Custom | Configurable | Vendor-specific extensions |
| ULID | Time + random | Yes | URL-safe, lexicographically sortable IDs |
Explanation
Why do UUIDs exist at all? Coordination, basically. Every node can mint its own ID without phoning a central allocator. That’s huge when you’ve got 50 microservices writing to the same database — nobody has to wait for a sequence counter, and you don’t need a central ID service that becomes a single point of failure.
v4 is built from cryptographically secure randomness. It’s unpredictable — great for secrets, not so great for databases. Every insert hits a random spot in the B-tree, which means page splits and cache misses once your table gets big enough. In my experience, this starts hurting around 10M rows on Postgres — your mileage may vary depending on hardware.
v7 places a Unix timestamp in the most significant bits and fills the rest with randomness. You end up with values that are roughly sorted by time while still being unique. The timestamp is millisecond-precision, so two IDs generated in the same millisecond still differ in the random portion.
ULID does the same thing but packs the value into a 26-character crockford-base32 string. A ULID is shorter than a UUID string and safe to use in URLs. Personally, I switched to ULIDs for public IDs a while back — they’re just easier to copy-paste in a terminal, and you never have to worry about URL encoding.
As database primary keys, sortable IDs keep related inserts near each other in B-tree indexes. That improves write throughput and cache locality compared to purely random v4 values. If you’re caching UUID-keyed responses at the edge, see our caching recipe for strategies that work well with time-ordered keys.
Variants
UUID as binary storage
import uuid
# Convert a UUID to its 16-byte representation for compact storage
uid = uuid.uuid7()
binary = uid.bytes # 16 bytes
uid_back = uuid.UUID(bytes=binary)
ULID string for URLs
import { ulid } from 'ulid';
// 26 chars, URL-safe, lexicographically sortable
const id = ulid();
console.log(`https://api.example.com/items/${id}`);
Snowflake-style IDs
If you need sortable 64-bit IDs, look at Twitter Snowflake. It relies on a central coordinator or a machine ID to avoid collisions.
Best Practices
- Reach for v7 or ULID when the ID is a database primary key. The time ordering keeps B-tree indexes from fragmenting.
- Store UUIDs as native
UUIDorBINARY(16)types, notCHAR(36)strings. In MySQL, going withBINARY(16)instead of a 36-char string cuts 20 bytes per row. On a 100M-row table, that’s roughly 2GB just in ID storage. - Generate IDs client-side only when the client needs them before the server responds.
- Validate UUID format when parsing external input.
- Keep sequential IDs internal and expose UUIDs for public-facing identifiers.
Common Mistakes
- Reaching for UUID v4 as a primary key without realizing the random insert penalty. I’ve seen this bite teams at scale — the index looks fine at 10K rows, then falls apart at 10M.
- Storing UUIDs as strings instead of compact binary types. This wastes space and bloats your
indexes — use
BINARY(16)or the nativeUUIDtype. - Using UUIDs in small, non-distributed tables where auto-increment integers are good enough.
- Generating UUIDs in a hot loop without caching the generator instance.
- Forgetting that UUID v1 leaks MAC and timestamp data — don’t use it for public IDs.
See Also
- RFC 4122 — the UUID spec from 2005. Covers versions 1-5 and the canonical string format. Worth skimming if you’re curious about the bit layout.
- ULID spec — encoding rules, monotonicity guarantees, and how ULID compares to UUID. The README is surprisingly readable.
- Python uuid module docs — covers
uuid4(),uuid7()(added in 3.13), and byte conversion. - MDN crypto.randomUUID() — native v4 generation in browsers and Node 19+ via Web Crypto.
- Database connection pooling recipe — our guide to connection pooling, which pairs with UUID keys for distributed database performance.
- Data validation recipe — our patterns for validating UUID format and other external input.
Frequently Asked Questions
Should I use UUID v4 or v7 for new projects?
Honestly, for database keys, just go with v7 or ULID — the time ordering alone cuts index fragmentation, which is usually the biggest pain point. v4 is still fine for things like session tokens where you don't care about sort order.
Are UUIDs truly unique?
For v4, the collision probability is roughly 1 in 2^122. Which is... a lot. We're talking generating billions of UUIDs every second for hundreds of years before a collision even becomes plausible. In most real workloads, you can stop worrying about it.
Can I use UUIDs in URLs?
Yes, but ULIDs are the better choice here — they're shorter and URL-safe out of the box. If you only have v4 or v7, drop the hyphens. It's not as pretty, but you get a 32-char string that does the job.
Do UUIDs affect database performance?
UUID v4 causes random B-tree inserts, which hurts write performance on large tables. UUID v7 and ULID are time-ordered, so their write performance is much closer to auto-increment integers.
Can I combine UUIDs with auto-increment IDs?
Yes. One common pattern is an auto-increment integer as the internal primary key for clustering performance, plus a UUID as the external-facing identifier for APIs and URLs.
Why does UUID v1 leak information?
UUID v1 embeds the MAC address of the machine that generated it, plus a 60-bit timestamp. If you expose v1 IDs publicly, someone can fingerprint the machine and figure out when IDs were created — not great for privacy. Stick with v4 or v7 for anything user-facing.
How do I generate UUIDs in a browser without dependencies?
Modern browsers and Node 19+ support crypto.randomUUID() natively. It returns a v4 UUID string
with no imports:
const id = crypto.randomUUID(); // '550e8400-e29b-41d4-a716-446655440000'
For v7 or ULID in the browser, you'll still need a library — the Web Crypto API only does v4.
Related Resources
Database Connection Pooling
Configure and tune database connection pools to maximize throughput while preventing connection exhaustion.
RecipeParse JSON
How to parse JSON strings into native data structures across multiple programming languages.
RecipeValidate and Sanitize User Input Data
How to validate, sanitize, and constrain user input data at the application boundary using schemas, type checking, and validation libraries.
RecipeCaching & Memoization in Python, JavaScript, and Java
How to cache expensive computations and API responses using in-memory LRU, TTL, and distributed caches across Python, JavaScript, and Java.
RecipeMerge JSON Files
How to merge multiple JSON files into a single object or array in Python, Java, and JavaScript.
PatternSingleton Pattern
Ensure a class has only one instance and provide global access to it. A creational design pattern for controlled object creation.