DEV Community

Guo
Guo

Posted on

UUID v4 vs v7: Choosing Identifiers for Modern Applications

UUIDs solve a simple problem: create identifiers without waiting for a database sequence or coordinating every new record through one central service.

The usual choice used to be UUID v4. It is random, widely supported, and easy to generate. UUID v7 adds a useful difference: its first bits contain a Unix timestamp in milliseconds, which makes values broadly time-sortable.

That makes v7 appealing for database records, event logs, and distributed systems. It does not make v4 obsolete.

The right choice depends on what the identifier needs to do after you create it.

UUID v4 is random

A UUID v4 has 128 bits in total. The version and variant consume some fixed bits, leaving 122 random bits.

A typical v4 UUID looks like this:

550e8400-e29b-41d4-a716-446655440000
Enter fullscreen mode Exit fullscreen mode

The 4 at the start of the third group identifies the version. The next group begins with an RFC-defined variant value.

The important property is that the value does not reveal when it was created or where it came from. With a cryptographically secure random source, UUID v4 is a good default when you need an identifier with no ordering requirement.

In browsers, crypto.randomUUID() generates a v4 UUID with a cryptographically secure random number generator. MDN documents its v4 behavior.

Use v4 when:

  • records do not benefit from creation-time order;
  • you want identifiers that do not expose their generation time;
  • your existing system, library, or database convention already uses v4;
  • simple random identifiers are enough.

UUID v7 puts time first

A UUID v7 looks familiar:

018f4a8b-79c0-7b92-8c7f-123456789abc
Enter fullscreen mode Exit fullscreen mode

The key difference is the first 48 bits. They contain a Unix timestamp in milliseconds. The remaining non-reserved bits contain random data.

That layout means UUID v7 values generated in different milliseconds generally sort in creation-time order when stored as their standard hexadecimal text representation or as comparable UUID bytes.

RFC 9562 defines v7 as a Unix-time-based UUID with a 48-bit millisecond timestamp and random bits for uniqueness. It also allows implementations to add a sub-millisecond fraction or a carefully seeded counter when stricter ordering within the same millisecond is needed. RFC 9562 is the current UUID specification.

Use v7 when:

  • you insert many records into an ordered index;
  • you want rough creation-time order without a separate timestamp field for every sort;
  • you process event streams, audit records, or append-heavy tables;
  • you want an identifier that is easier to inspect during debugging.

"Time-sortable" is not "strictly ordered"

This distinction matters.

Suppose an application creates three v7 UUIDs in the same millisecond:

018f4a8b-79c0-7b92-...
018f4a8b-79c0-7a11-...
018f4a8b-79c0-7f88-...
Enter fullscreen mode Exit fullscreen mode

They share the same timestamp prefix. Their suffixes are random unless the generator implements an additional monotonic counter or sub-millisecond ordering strategy.

ToolExo's UUID v7 generator uses the timestamp first and secure random data for the remaining payload. It does not claim to preserve creation order among UUIDs generated within the same millisecond.

If your business logic needs a strict sequence, use an explicit sequence number, a database-generated ordering column, or a timestamp plus a deliberate tie-breaker. Do not infer strict event order from a v7 UUID alone.

Database locality is a reason, not a guarantee

Random v4 UUIDs spread inserts across an index. That can be useful for distribution, but it may also produce less localized writes in some storage engines.

UUID v7 values tend to cluster by creation time. For append-heavy workloads, that can reduce the amount of index churn compared with random identifiers.

The practical result depends on your database, index type, storage layout, write pattern, and concurrency. It is worth testing with representative data rather than assuming that v7 will improve every workload.

Also keep the creation timestamp as a separate column when your application needs precise filtering, reporting, retention policies, or database queries such as:

WHERE created_at >= NOW() - INTERVAL '7 days'
Enter fullscreen mode Exit fullscreen mode

A UUID v7 can help with ordering. It should not become a replacement for explicit data modeling.

v7 exposes approximate creation time

A v4 UUID reveals no timestamp. A v7 UUID reveals its creation millisecond.

That is useful when you inspect logs:

018f4a8b-79c0-7b92-8c7f-123456789abc
Enter fullscreen mode Exit fullscreen mode

A UUID inspector can read the timestamp prefix and show when the value was generated.

It can also be a privacy consideration. If an identifier appears in a public URL, a client can infer roughly when the record was created. That may be harmless for a blog post or public event, but less desirable for some user-facing records.

Choose v4 if hiding generation time matters more than chronological locality.

UUIDs are identifiers, not secrets

A UUID identifies something. It should not be the only proof that someone is allowed to access it.

Do not use a UUID as:

  • a password;
  • an API key;
  • a session credential;
  • a password-reset token;
  • authorization logic by itself.

A cryptographically random UUID has a large random space, but identifiers are often logged, copied into URLs, stored in browser history, or exposed through APIs. Authentication and authorization need purpose-built controls.

Generate with a secure source

Do not build UUIDs from Math.random().

Math.random().toString(16)
Enter fullscreen mode Exit fullscreen mode

is not a cryptographically secure source. In the browser, use Web Crypto APIs such as crypto.randomUUID() for v4 or crypto.getRandomValues() when implementing an RFC-compliant generator yourself. MDN explains getRandomValues().

I built the ToolExo UUID Generator to generate RFC 9562 v4 and v7 UUID batches locally in the browser. It can also inspect an existing value, identify its version and variant, and show the embedded timestamp for a valid v7 UUID.

A practical choice

Use UUID v4 when you want a random identifier with no embedded creation time.

Use UUID v7 when chronological locality and rough time ordering are useful, especially for append-heavy records and operational debugging.

Keep a separate created_at field when time is part of your data model. Use a sequence or explicit ordering key when order within the same millisecond matters. And keep UUIDs out of authentication decisions.

That gives each identifier one clear job, which is usually where UUID design goes right.

Top comments (1)

Collapse
 
alexshev profile image
Alex Shev •

The choice is usually about system behavior, not just uniqueness. UUIDv7’s time ordering can improve index locality and operational debugging, but it also makes creation time more legible and needs a clear migration story if identifiers cross trust boundaries. Naming those trade-offs up front helps teams avoid treating a format change as a cosmetic refactor.