What is a GUID / UUID?
At its simplest, a UUID (Universally Unique Identifier) is a 128-bit number. When you see it in a log file or an API response, it is usually represented as a 36-character string of hexadecimal digits separated by hyphens (e.g., 550e8400-e29b-41d4-a716-446655440000).
A GUID (Globally Unique Identifier) is essentially the same thing. The term "GUID" was popularized by Microsoft, while "UUID" is the standard defined by the IETF (Internet Engineering Task Force). For 99% of engineering conversations, they are interchangeable.
The goal of a UUID is to allow any system to generate an identifier without needing to coordinate with a central authority. In a traditional SQL setup, the database is the authority: it knows that the last ID was 100, so the next one is 101. In a distributed architecture (think microservices, offline mobile apps, or multi-region shards), that coordination becomes a massive bottleneck. UUIDs shift the responsibility of identity generation from the database to the application layer.
Who uses it?
Almost everyone in the distributed space. If you are working with the following, you are likely using UUIDs:
- Microservices Architects: When Service A needs to create a record that Service B will eventually process, they cannot wait for a database return value from a remote shard. They generate the UUID in the app layer and pass it through the event bus (Kafka, RabbitMQ).
- Frontend Engineers: If you are building a "Draft" feature in a React or Vue app where the user can create an object offline, you cannot have a
nullID. You generate a UUIDv4 on the client, and when the user hits "Save," the database accepts that ID as the primary key. - Security Engineers: UUIDs are used for session tokens, API keys, and password reset identifiers. Because the entropy is so high, it is computationally impossible for an attacker to "guess" a valid ID by simply incrementing a number (a vulnerability known as ID enumeration).
- Cloud Infrastructure Providers: AWS, Azure, and GCP use UUIDs for almost every resource ID (EC2 instances, S3 buckets, etc.) because they are managing millions of resources across thousands of physical clusters.
How is it used?
In a professional stack, you will see UUIDs used in three primary ways:
1. As a Primary Key (PK) The ID is the unique handle for the row. This is where the performance trade-offs happen. If you use a random UUID as a clustered index, you are introducing fragmentation. If you use a sequential UUID (like v7), you are keeping your B-Tree healthy.
2. As a Correlation ID
This is a lifesaver for debugging. When a request enters your system, the gateway generates a UUID and attaches it to the header (e.g., X-Correlation-ID). Every single log entry across every microservice includes this ID. When a customer reports a bug, you search that one UUID in your ELK stack or Datadog, and you see the entire lifecycle of the request across five different services.
3. As a Public Handle (Slug)
Instead of exposing myapp.com/user/123, you expose myapp.com/user/f47ac10b.... This prevents competitors from scraping your user base by simply iterating through integers.
When and why was it created?
The UUID standard was created to solve the "collision" problem in a world where computers started talking to each other without a central master. In the early days of networking, if two different companies created a database of "Customer 1," and then those companies merged, you had a collision.
The original specifications focused on creating a way to combine time, hardware addresses (MAC addresses), and randomness to ensure that the probability of two people generating the same ID was lower than the probability of a meteor hitting your data center at the exact moment you hit "Enter."
The drive was purely about decentralization. The ability to generate an ID in a vacuum (without a network connection and without a central lock) is what enabled the scale of the modern web.
How unique is it in the real world?
Developers often ask, "But what if two IDs actually collide?"
Let's talk about UUIDv4, which uses 122 bits of entropy. The number of possible combinations is 2 to the power of 122. To give you a sense of that scale: if you generated 1 billion UUIDs every second for the next 100 years, the probability of creating just one collision is still effectively zero.
In the real world, you are more likely to have a hardware failure in your RAM that flips a bit and creates a duplicate than you are to have a natural UUIDv4 collision. If you experience a collision, check your code; it is almost always because someone used a bad random number generator (PRNG) instead of a cryptographically secure one (CSPRNG), or they are accidentally seeding their generator with a constant value.
Is it suitable for databases?
This is where the "experienced engineer" opinion comes in: UUIDs are great for logic, but they can be lethal for storage if you are careless.
Most relational databases (MySQL, SQL Server, PostgreSQL) use B-Tree structures for their indexes. B-Trees love sequential data. When you insert 1, 2, 3..., the database just appends the data to the end of the page.
When you insert random UUIDv4s, you get Index Fragmentation. The database has to insert the new ID into the middle of the tree. This forces "page splits," where the DB has to move half of a page's data to a new page to make room.
The results are:
- Write Amplification: You are doing way more disk I/O than necessary.
- Bloated Indexes: Your indexes end up with lots of empty space (low fill factor), meaning they take up more RAM and slow down reads.
- Cache Misses: Because the data is scattered randomly across the disk, your buffer pool becomes inefficient.
The Verdict by Engine:
- If you use PostgreSQL: It is a great fit. Postgres has a native
UUIDtype and handles the storage efficiently. - If you use MySQL (InnoDB): Be very careful. Using a random UUID as a clustered primary key will eventually kill your performance. Use a sequential UUID (like v7) or use a
BIGINTas the PK and the UUID as aUNIQUEsecondary index. - If you use SQL Server: Similar to MySQL. Avoid using
NEWID()as a clustered index. UseNEWSEQUENTIALID()if you can, or move to a v7-style identifier.
Comparing UUID Types
Not all UUIDs are created equal. Choosing the wrong version is a common junior mistake.
UUIDv1 (Time + MAC)
It uses the system clock and the MAC address of the machine.
- Pros: Sortable-ish.
- Cons: Massive security risk. You are literally leaking the hardware ID of your server in every single ID. In the modern cloud (where MACs are virtualized), it is also less reliable.
UUIDv3 and v5 (Name-based)
These are deterministic. They hash a namespace and a name (MD5 for v3, SHA-1 for v5).
- Pros: If you provide the same input, you get the same output. Great for creating stable IDs from external strings.
- Cons: Not random. Not for primary keys.
UUIDv4 (Random)
The most common version. Just 122 bits of randomness.
- Pros: Simple, no coordination, extremely low collision risk.
- Cons: Randomness = Index Fragmentation. No chronological information.
UUIDv6 (Reordered v1)
A newer attempt to make v1 sortable by rearranging the time bits.
- Pros: Better for databases than v1 or v4.
- Cons: Still carries some of the legacy baggage of v1.
UUIDv7 (The Modern Choice)
The current "industry best practice" for distributed systems. It puts a millisecond-precision timestamp at the beginning, followed by randomness.
- Pros: Lexicographically sortable. No index fragmentation. High entropy.
- Cons: Leaks the creation time of the record (usually a feature, not a bug).
ULID (Universally Unique Lexicographically Sortable Identifier)
Very similar to v7 but uses a different encoding (Base32) to make it shorter and more human-readable.
- Pros: Shorter string representation, sortable.
- Cons: Not a formal RFC standard like the UUID versions.
NanoID (Random String)
Not a UUID. It is a compact random string generator.
- Pros: Much shorter, customizable alphabet, very fast.
- Cons: No timestamp. Same fragmentation issues as UUIDv4 if used as a database PK.
Comparison Summary Table
| Type | Logic | Sortable | DB Friendly | Entropy Source | Use Case |
|---|---|---|---|---|---|
| UUIDv1 | Time+MAC | Partial | Medium | Hardware | Legacy Systems |
| UUIDv3 | Hash(MD5) | No | Medium | Deterministic | Stable Aliases |
| UUIDv4 | Random | No | Poor | CSPRNG | Session Tokens |
| UUIDv5 | Hash(SHA1) | No | Medium | Deterministic | Stable Aliases |
| UUIDv6 | Time-Reordered | Yes | High | Time+Random | Sequential IDs |
| UUIDv7 | Time+Random | Yes | High | Time+CSPRNG | Distributed PKs |
| ULID | Time+Random | Yes | High | Time+CSPRNG | Public IDs/URLs |
| NanoID | Random | No | Poor | CSPRNG | URL Slugs |
Final Architectural Advice
If you are starting a project today, here is the blueprint I recommend:
- For Internal Database Primary Keys: Use UUIDv7. It gives you the distributed nature of a UUID but the performance of an integer. Your DBAs will thank you.
- For Public URLs: Use ULID or NanoID. You want something short and clean that does not look like a wall of hex.
- For Security Tokens: Stick with UUIDv4. You want pure randomness with no timestamps that could leak information about when a token was generated.
- Storage Tip: Always store your UUIDs as
BINARY(16)or a nativeUUIDtype. Storing them asVARCHAR(36)is an amateur move that wastes 2x the storage and slows down your joins.
The goal of a senior engineer is not just to make the code work, but to make the system sustainable. Choosing the right ID type is one of the easiest ways to ensure your system does not collapse under its own weight once you hit a million rows.
More Information About GUIDs
Globally Unique Identifier - Wikipedia, the free encyclopedia
GUID Structure - Microsoft.com
RFC 9562 (2024-05)
RFC 4122 (2005-07)