When not to use a UUID

UUID text8 sectionsUpdated

A UUID buys you identifiers without coordination, for 16 bytes. If you have coordination anyway, or you need a shorter or unguessable value, one of five other formats fits better.

The interesting question is not which UUID version, but whether an identifier that nobody has to hand out is worth its size at all. Here is the honest comparison, including the boring answer that often wins.

What the converter readscomputed by the converter itself
in01890a5d-ac96-774b-bcce-b302099a8057outuuid · RFC 9562 (10xx) · version 7

v7, the identifier the alternatives are measured against

in0c5b2444-70a0-4932-980c-b4dc0d3f02b5outuuid · RFC 9562 (10xx) · version 4

v4, no structure at all

Open the converter: UUID textNothing you paste leaves the browser.
On this page (8)

The field#

FormatBitsTextSorts by timeNeeds coordinationGood at
UUID v412836nonobeing available everywhere
UUID v712836yesnodatabase keys
ULID12826yesnoshort text, human handling
Snowflake6419 digitsyesyes, a worker id per nodefitting a bigint column
KSUID16027yesnoa second-resolution prefix plus 128 random bits
NanoIDconfigurable21 by defaultnonoshort, URL-safe, tunable entropy
CUID2configurable24 by defaultnonocollision resistance without leaking time
auto-increment64shortyesyes, one sequencesmall, fast, obvious

Snowflake, when 64 bits matter#

A Snowflake packs a 41-bit millisecond timestamp, a 10-bit machine id and a 12-bit per-millisecond counter into 64 bits, with the top bit left at zero so the value stays a positive bigint. That halves the index size against a UUID and prints as about nineteen digits. The price is coordination: every node needs a distinct worker id, and handing those out reliably is its own small system. Choose it when the storage saving is measured and the operational cost is acceptable.

KSUID and the second-resolution middle ground#

KSUID is 20 bytes: a 32-bit second counter, offset from its own epoch in 2014 rather than from 1970, and a 128-bit random payload, printed as 27 base62 characters that sort lexicographically by time. It predates v7 and remains reasonable, but v7 does the same job in fewer bits with a standard behind it.

NanoID and CUID2, when the value faces users#

Both aim at short, URL-safe identifiers rather than at structure. NanoID is 21 characters of a 64-symbol alphabet by default, chosen to land near a v4 for collision probability, and the length is a parameter. CUID2 defaults to 24 characters and hashes every entropy source into the result, so neither the time nor the host can be read back out. Neither sorts, and neither fits a native UUID column: they are text identifiers, and that is the point.

RFC 9562 § 6.9 · UnguessabilityUnguessability, the axis on which CUID2 and NanoID differ from a UUID

The boring answer#

If there is exactly one database, one writer, and no plan to merge data from elsewhere, an auto-increment integer is smaller, faster, and easier to read in a log than anything on this list. UUIDs buy independence from coordination; when there is no coordination problem, you are paying for insurance you do not need.

RFC 9562 § 6.7 · Collision ResistanceCollision resistance, and what "no coordination" actually costs

Questions people actually ask#

Is Snowflake better than UUID v7?

It is half the size and fits a bigint, but every node needs a coordinated worker id. v7 needs no coordination at all.

What should I use if the identifier must not reveal creation time?

v4, NanoID or CUID2. Anything time-ordered (v1, v6, v7, ULID, KSUID, Snowflake) reveals when it was made.

Are UUIDs overkill for a single database?

Often, yes. With one writer and no data merging, an auto-increment key is smaller and simpler; UUIDs pay for independence you are not using.

Browse the reference