Most teams reach for UUID v4 by default. It is random, universally supported, and easy to generate. For years, that was enough.
But modern systems ask more from identifiers than simple uniqueness. Database index locality, insertion order, URL length, readability, cross-service interoperability, and sortability all affect architecture decisions. Choosing the wrong format can create avoidable index fragmentation in write-heavy tables, awkward URLs that frustrate users, or unnecessary complexity when a simpler format fits the workload better.
Today, four formats dominate the conversation: UUID v4, UUID v7, ULID, and Nano ID. Each makes different trade-offs. This guide helps you pick the right one based on your actual system boundaries and workload, not hype.
TL;DR
- UUID v4— solid general-purpose random identifier. Still the safe default when ordering does not matter.
- UUID v7— time-ordered UUID. Often a better fit for database keys and ordered event streams because it improves index locality.
- ULID— compact (26 chars), lexicographically sortable. Popular in log and event workflows where readable sort order matters.
- Nano ID— short, URL-friendly, customisable length. Best for compact public identifiers rather than standard UUID formatting.
Need IDs right now? Use the CodeAva ID Generator to create UUID v4, UUID v7, ULIDs, and Nano IDs instantly.
The core trade-off: uniqueness is not the only requirement
Developers usually start by asking “Is it unique?” For any of these four formats, the answer is yes (with appropriate length and entropy). But in real systems, uniqueness is table stakes. The harder questions are:
- Will this be inserted into a database index? Random values scatter writes across B-tree pages. Time-ordered values append near the end.
- Does sort order matter?If you need to query “most recent” without a separate timestamp column, the ID itself needs to encode time.
- Will users see it in URLs? A 36-character UUID works but is verbose. A 10-character Nano ID is compact.
- Do you need a standard 128-bit UUID format? Some systems, libraries, and column types specifically expect canonical UUID formatting.
- Is compactness more important than compatibility? Shorter IDs save bytes in URLs, logs, and payloads but may sacrifice interoperability or collision tolerance.
There is no universally best format. The right choice depends on the system boundary and workload.
Quick comparison: UUID v4 vs UUID v7 vs ULID vs Nano ID
| Format | Sortable? | Length / shape | Best for | Main trade-off |
|---|---|---|---|---|
| UUID v4 | No | 36 chars, hex + hyphens | General-purpose distributed IDs | Random insertion → weaker index locality |
| UUID v7 | Yes (time-ordered) | 36 chars, hex + hyphens | Database keys, ordered events | Newer standard, limited native support |
| ULID | Yes (lexicographic) | 26 chars, Crockford Base32 | Event logs, time-series keys | Not an IETF UUID standard |
| Nano ID | No | Configurable (default 21 chars) | Short public IDs, URLs, slugs | Collision risk if length is too short |
UUID v4: still useful, but not always ideal
UUID v4 generates 122 random bits (plus 6 bits for version and variant markers), producing identifiers like 550e8400-e29b-41d4-a716-446655440000. It is the most widely supported UUID version: every major language, database, and framework has native or near-native support.
For many workloads, UUID v4 is still the right choice. It is simple to generate, requires no clock coordination, and provides strong uniqueness guarantees across distributed nodes without any shared state.
Where UUID v4 works well
- General-purpose resource identifiers in APIs where ordering is irrelevant
- Correlation IDs for distributed tracing where you care about uniqueness, not sort order
- Any system where the ID is not the primary sort or index key in a write-heavy table
Where UUID v4 can be a problem
Random insertion order means that new rows land at scattered positions across a B-tree index. In write-heavy tables with millions of rows, this causes more page splits, higher write amplification, and larger index sizes compared to sequentially ordered keys.
This does not mean UUID v4 is bad for databases. Many systems run fine with random UUIDs. But if you are designing a new table where insertion order and index efficiency matter, it is worth evaluating UUID v7 or ULID before defaulting to v4.
UUID v7: why developers are paying attention now
UUID v7 is defined in RFC 9562 (the current UUID specification). It encodes a Unix millisecond timestamp in the first 48 bits and fills the remaining bits with random data. The result is a standard 128-bit UUID that is naturally ordered by creation time when compared lexicographically.
Practical benefits
- Better index locality. New rows append near the end of B-tree indexes instead of scattering randomly. This reduces page splits and improves write throughput in PostgreSQL, MySQL InnoDB, and similar engines.
- Built-in time ordering. You can sort by creation time using the ID column alone, without adding a separate
created_attimestamp. This simplifies pagination and event ordering queries. - UUID ecosystem compatibility. UUID v7 keeps the familiar
xxxxxxxx-xxxx-7xxx-yxxx-xxxxxxxxxxxxformat. Existing UUID column types, validation logic, and tooling work without modification.
A note on timing
When UUID v7 is a strong fit
- Write-heavy relational tables where index efficiency matters
- Event records, audit logs, and time-series data
- Systems where you want to order by creation time via the primary key
- Any workload that currently uses UUID v4 but suffers from index fragmentation
UUID v7 is not universally better than v4. If your system does not benefit from ordering and your existing v4 keys work well, there is no urgent reason to switch. Choose it because it fits the workload, not because it is newer.
ULID: compact and lexicographically sortable
ULID (Universally Unique Lexicographically Sortable Identifier) encodes a 48-bit timestamp and 80 bits of randomness into a 26-character Crockford Base32 string like 01ARZ3NDEKTSV4RRFFQ69G5FAV. The encoding is designed so that string comparison preserves chronological order.
Why teams choose ULID
- Compact. 26 characters vs 36 for a canonical UUID. Saves bytes in logs, URLs, and payloads.
- Lexicographically sortable. Simple string sorting produces chronological order. No special comparator needed.
- Human-readable sort order. Scanning a list of ULIDs visually shows oldest-to-newest without parsing.
- No ambiguous characters. Crockford Base32 excludes I, L, O, and U to reduce transcription errors.
Important caveats
ULID is not a UUID standard. It does not conform to RFC 9562 and cannot be stored in a native UUID column type without conversion. If downstream tools, APIs, or schemas specifically require canonical UUID formatting, ULID will cause friction.
Teams should choose ULID intentionally for its compact sortable representation, not as a drop-in UUID replacement. If you want time-ordered identifiers and UUID compatibility, UUID v7 gives you both.
Nano ID: compact public IDs and URL-friendly use cases
Nano ID generates short, URL-safe identifiers using a configurable alphabet and length. The default configuration produces 21 characters from A-Za-z0-9_-, but both can be customised.
Where Nano ID excels
- Public-facing identifiers. Invite codes, share links, short URLs, and compact object references.
- URL slugs. Short and clean in browser address bars.
- Frontend-generated IDs. Lightweight client-side generation with minimal bundle impact.
What Nano ID does not solve
Nano ID is not time-ordered. It does not provide sortability by creation time. It is not a UUID and will not satisfy UUID format requirements. If you need database-first, time-ordered identifiers, UUID v7 or ULID are better choices.
The configurable length is both an advantage and a risk. Shorter IDs increase collision probability. A 10-character Nano ID with the default 64-character alphabet has a meaningful collision probability at hundreds of millions of IDs. Always reason about your expected volume before choosing a short length.
Decision guide: which one should you choose?
Work through these questions to narrow the decision:
Do you need a standards-based UUID?
→ UUID v4 or UUID v7. Both conform to RFC 9562.
Does insertion order or index locality matter?
→ UUID v7 or ULID. Both are time-ordered.
Do you need lexicographic sortability as plain strings?
→ ULID. Designed for string-comparison sorting.
Will the ID appear in public URLs?
→ Nano ID (compact) or ULID (compact and sortable).
Is compactness more important than UUID compatibility?
→ ULID (26 chars) or Nano ID (customisable length).
Do you want broad native library support?
→ UUID v4. Supported natively in virtually every language and database.
Quick decision matrix
| Workload | Recommended format |
|---|---|
| General-purpose distributed IDs | UUID v4 |
| Ordered database / event records | UUID v7 |
| Compact sortable string IDs | ULID |
| Short URL-friendly public IDs | Nano ID |
Real-world scenarios
Write-heavy relational database table
A payments service inserting millions of rows per day into a PostgreSQL table with a UUID primary key. Random UUID v4 values scatter writes across the B-tree index, increasing page splits and write amplification. UUID v7 appends new rows near the end of the index, reducing fragmentation and improving throughput.
Append-heavy event stream
An event-sourcing system that appends events to an ordered log. Events need to be queryable by time range and sortable by insertion order. ULID or UUID v7 both work well. ULID is more compact in string form; UUID v7 is more compatible if the event store uses native UUID columns.
Public share URL or invite code
A collaboration tool that generates shareable invite links like app.example.com/invite/Xk9_mQ3pLz. The ID needs to be short, URL-safe, and unpredictable. Nano ID is the natural fit: compact, no special characters, and configurable length.
Internal correlation IDs
A microservices platform that passes correlation IDs through request headers for distributed tracing. Ordering does not matter; uniqueness across services does. UUID v4 is the simplest and most broadly compatible choice.
Object IDs in a multi-service API
An API gateway that assigns resource IDs consumed by multiple downstream services. If downstream services expect UUID formatting, UUID v7 gives you compatibility plus ordering. If no UUID format is required and compactness matters, ULID or Nano ID may be a better fit depending on whether sortability is needed.
Implementation examples
Concise, copy-pasteable snippets for generating these IDs in popular languages.
JavaScript / Node.js
// UUID v4 (native)
crypto.randomUUID();
// Nano ID (npm: nanoid)
import { nanoid } from 'nanoid';
nanoid(); // 21 chars, URL-safe
nanoid(10); // custom length
// ULID (npm: ulid)
import { ulid } from 'ulid';
ulid();Python
import uuid # UUID v4 str(uuid.uuid4()) # ULID (pip: python-ulid) from ulid import ULID str(ULID())
Go
import "github.com/google/uuid" // UUID v4 id := uuid.New().String() // UUID v7 id7, _ := uuid.NewV7() fmt.Println(id7.String())
Native UUID v7 support varies
google/uuid package supports it. In JavaScript, crypto.randomUUID() generates v4 only; for v7, use a library or the CodeAva ID Generator for quick generation. Python's uuid module does not include v7 natively as of 3.12; community packages fill the gap.Common mistakes
- Assuming all UUID-like IDs have the same database behaviour. UUID v4 and UUID v7 are both 128-bit UUIDs, but they have very different index-insertion patterns. Benchmarking matters for write-heavy workloads.
- Choosing Nano ID without thinking about collision tolerance. A shorter ID means higher collision probability at scale. If you are generating millions of IDs, reason about the math before setting a short length.
- Assuming lexicographic sorting always equals exact chronological order. UUID v7 and ULID sort by creation time at millisecond granularity. IDs generated within the same millisecond may not preserve strict insertion order without a monotonic counter.
- Using a non-standard format where downstream tools require canonical UUIDs. If your database column type is
UUID, ULID and Nano ID will not fit without conversion. Check your schema constraints before choosing. - Choosing by hype rather than by storage, query, and API needs. UUID v7 is not always better than v4. ULID is not always better than UUID. Match the format to the workload.
Compare formats side by side
If you are still deciding between formats, generate UUID v4, UUID v7, ULIDs, and Nano IDs side by side in the CodeAva ID Generator and compare them in the same workflow. Bulk generation, one-click copy, and format badges make it easy to see the practical differences.
Conclusion
Pick the ID format that matches the system boundary and workload:
- UUID v4 for broad general-purpose randomness where ordering does not matter.
- UUID v7 when ordering and index locality matter, and you want to stay in the UUID ecosystem.
- ULID when you want compact, sortable string identifiers and do not need canonical UUID compatibility.
- Nano ID when you want short, URL-friendly identifiers for public-facing use cases.
None of these formats is obsolete or universally better. The right choice is the one that fits your schema, your queries, your API contract, and your users.
Generate IDs with the CodeAva ID Generator or explore more developer tools.




