UUID and ULID Generator Guide: Unique Identifiers Explained

Why unique identifiers are needed

Almost every system that stores records needs a way to refer to each record unambiguously. A database of orders, a set of uploaded files, a queue of background jobs, or a log of API requests all need identifiers that never collide, even when records are created on different servers at the same moment. Sequential numbers, one, two, three, work on a single server but fail in distributed systems, because two servers can both assign the next number, and merging their data then produces duplicates. They also leak information, revealing how many records exist and in what order they were created.

Universally unique identifiers, UUIDs, solve this by making collisions so unlikely that they can be ignored in practice, without any coordination between the machines generating them. A UUID & ULID Generator produces such identifiers on demand, in bulk, in the standard formats, and with the version and variant bits set correctly. The tool is a format utility: it generates identifiers, but the decision about which kind of identifier a system should use, and how it should be stored, belongs to the system's design.

ULIDs, universally unique lexicographically sortable identifiers, are a newer alternative that combine a timestamp with randomness. A ULID encodes the creation time in its first characters, so ULIDs sort in roughly chronological order, which is convenient for databases that index by identifier and for logs that are read in order. UUID version seven offers a similar time-ordered property within the UUID format, and the choice between ULID and time-ordered UUID is often a matter of ecosystem and tooling rather than a fundamental difference.

The UUID format

A UUID is a 128-bit number, usually written as thirty-two hexadecimal digits arranged in five groups separated by hyphens, in the pattern eight digits, four digits, four digits, four digits, twelve digits. A well-known illustrative example is 550e8400-e29b-41d4-a716-446655440000, which shows the grouping clearly: eight characters, then four, then four, then four, then twelve, thirty-two in total. The hyphens are presentation, not part of the value, and some systems store UUIDs without them, as a continuous thirty-two-character string, or as raw sixteen-byte binary, which is more compact.

Within the digits, certain positions carry meaning. The version is encoded in the first digit of the third group: a four there means version four, a seven means version seven, and a one means version one. The variant is encoded in the first digit of the fourth group, which for the standard variant is eight, nine, a, or b. These fixed bits reduce the random portion slightly, but the remaining randomness is still vast: a version four UUID has one hundred and twenty-two random bits, giving about five thousand billion billion billion billion possible values, a space so large that randomly generated collisions are not a practical concern.

Version one UUIDs combine a timestamp with a machine identifier, historically a network address, which makes them unique but also reveals when and where they were generated, a privacy drawback that has made them uncommon. Version three and version five UUIDs are derived deterministically from a namespace and a name by hashing, so the same name always gives the same identifier, which is useful for stable references but not for unpredictable identifiers. Version four UUIDs are randomly generated and are the default choice for most purposes. Version seven UUIDs combine a Unix timestamp in milliseconds with randomness, giving time-ordering with strong uniqueness.

A worked example of reading a UUID

Read the illustrative UUID 550e8400-e29b-41d4-a716-446655440000 from left to right. The five groups have lengths eight, four, four, four and twelve, which sum to thirty-two hexadecimal digits. The third group begins with four, marking it as a version four UUID. The fourth group begins with a, which falls in the standard variant range of eight, nine, a and b. The remaining digits are the random portion, and changing any one of them produces a different identifier that refers, by design, to nothing at all until it is assigned to something.

Generating a batch makes the uniqueness property tangible. Generate one hundred UUIDs and no two will match, and generating a million will still show no duplicates, because the space is so large that the chance of a collision among a million random version four UUIDs is roughly one in ten to the power of twenty-five, far below the chance of a hardware error corrupting the data. That is what practically unique means: not mathematically impossible to collide, but so unlikely that no system needs to plan for it, unlike sequential numbers, where collisions are certain without coordination.

ULIDs look different: they are twenty-six characters drawn from a Crockford base-thirty-two alphabet, which excludes easily confused characters such as I, L, O and U. The first ten characters encode a forty-eight-bit timestamp in milliseconds, and the remaining sixteen encode eighty bits of randomness. A ULID is therefore both an identifier and a rough creation timestamp, readable without a database lookup, though the timestamp is only to millisecond precision and should not be treated as a substitute for a proper created-at field where exact timing matters.

Choosing between UUID, ULID and sequential numbers

Sequential numbers remain the right choice for a single database where human-readable, short identifiers are valued and where the count of records is not sensitive. They are compact, they sort naturally, and they are easy to read over the phone. Their weaknesses are collisions on merge, predictability, and information leakage, and any system that exposes sequential identifiers in URLs should consider whether revealing the order and volume of records is acceptable.

Random version four UUIDs are the right default for distributed systems, for identifiers exposed in URLs or APIs where unpredictability matters, and for data that will be merged from multiple sources. Their weaknesses are length, which makes them awkward to read aloud, and random ordering, which fragments database indexes when used as a primary key at large scale, because each new row lands in a random index position rather than at the end. Time-ordered version seven UUIDs or ULIDs address the index problem by keeping new identifiers near each other in sort order while retaining distributed uniqueness.

Storage matters too. Storing a UUID as a thirty-six-character string with hyphens costs more space and compares more slowly than storing it as sixteen raw bytes, and at very large scale that difference is worth addressing with a binary column type where the database supports it. For most applications, however, the string form is simpler, more portable and easier to debug, and the storage cost is negligible compared with the rest of the record. Choose the binary form when scale demands it, not by default.

Practical guidance

Generate identifiers with a reputable library or tool rather than inventing a format, because hand-rolled identifiers frequently contain subtle flaws, such as insufficient randomness, a missing version marker, or a timestamp with too little resolution. Validate identifiers received from outside a system, rejecting malformed values early rather than letting them reach a database query. Never use an identifier as a secret: a UUID in a URL is an identifier, not an access control, and anything that must stay private needs proper authentication regardless of how unguessable its identifier looks.

Related tools include the Hash Generator for deterministic fingerprints, which differ from random identifiers in that the same input always gives the same hash, and the guide to API Pagination Test Data for generating realistic identifiers in test fixtures. Identifiers are infrastructure: invisible when they work, and the source of painful merges and collisions when they do not, so choosing a standard format and generating it correctly is effort well spent.

For the surrounding development work, the Password Generator creates strong secrets, which unlike identifiers must be kept private, and the QR Code Generator encodes short identifiers into scannable form.