UUID Generator Guide: Batch v4 IDs Done Right
If all you need is a batch of unique random IDs, generate them and move on. Read this when you have to decide which kind of ID to use, how to store it, and whether it is safe to hand out.
When you actually need a UUID
UUIDs earn their keep when several places have to mint an identifier without asking a central authority first: a browser creating a draft while offline, a phone queueing records on a bad connection, two services inserting into the same table, one request traced across five logs, an opaque handle handed to a partner. What these share is that nobody wants to, or can, go ask a database for the next number.
For a single database with modest write volume, a plain auto-increment bigint is smaller, indexes faster, and is far easier to read in a support ticket. Anything a human has to say out loud — order numbers, tracking numbers, invite codes — should not be a UUID either. Nobody reads 36 characters correctly over the phone.
- Good fit: distributed primary keys, offline creation, trace IDs, idempotency keys, test fixtures
- Poor fit: numbers people read or dictate, short links, invite codes
- Never: passwords, API keys, session tokens
Generating and storing them without mistakes
A v4 UUID is 32 hexadecimal characters in 8-4-4-4-12 groups. The 13th character is always 4 — the version — and the 17th is one of 8, 9, a or b, the variant bits. If either position looks different, you are not looking at a standard v4.
Upper and lower case mean the same value, but pick one convention before the data reaches storage. Mixed case in a case-sensitive collation turns one identifier into two rows that never match. Lowercase is the safer default, since that is what the uuid type in Postgres and most libraries emit.
- Store as a native uuid type first, binary(16) second, char(36) last
- Do not wrap the column in md5() or substring() just to make an index behave
- Skip manual de-duplication; the real failures come from truncating the string
- Agree on hyphens across services — one side keeping them and the other stripping them makes joins return zero rows
Limits: random order, and unguessable is not secure
Because v4 values are uniformly random, every insert lands at a random point in the index. That means page splits and a bigger index; on write-heavy tables it is measurably slower than an append-only key. UUIDv7 puts a millisecond timestamp first and ULID does the same in a shorter encoding, so both stay sortable by creation time.
The other common misreading is that unguessable means safe. A UUID never expires, cannot be revoked, and leaves no issuance record, which makes it a poor password-reset token or API key. Those need a server-issued value that is single-use, time-limited and can be invalidated on demand.
FAQ
Can a UUID v4 actually collide?
The version carries 122 random bits. Treat collisions as impossible in practice: even at a billion IDs a second for years, the odds stay far below the failure rate of the hardware generating them. Real duplicates come from truncated strings or hand-rolled IDs built from a timestamp and a counter.
Can I use a UUID as a password or API key?
No. A UUID has no expiry, no revocation and no record of issuance, so a leak cannot be contained, and 36 characters is unusable for a person. Use a server-side token with an expiry and a single-use flag instead.
UUID, auto-increment, or ULID?
Auto-increment for one database with heavy writes and IDs humans read. UUID when several clients must create IDs independently. UUIDv7 or ULID when you need uniqueness and roughly time-ordered inserts to keep the index healthy.
Related tool:UUID Generator