UUIDs Are Just Numbers
If you've worked with databases or APIs, you've probably seen UUIDs everywhere. Something like:
550e8400-e29b-41d4-a716-446655440000 They look like some arcane string format with dashes in specific places. But here's the thing: a UUID is just a 128-bit number, and the familiar string representation is just that number written in base 16 (hexadecimal) with a few hyphens thrown in for readability.
What's actually in a UUID?
UUID stands for Universally Unique Identifier. The spec (RFC 9562) defines it as a 128-bit value — that's 16 bytes, or 32 hexadecimal digits. The canonical string form groups those 32 hex digits into five sections separated by hyphens:
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
8 chars 4 4 4 12 chars If you strip the hyphens, you're left with exactly 32 hex characters. Each hex character encodes 4 bits, so 32 × 4 = 128 bits. That's it. The entire UUID is one big number.
Hexadecimal is just base 16
We normally write numbers in base 10 (decimal), using digits 0
through 9. Hexadecimal (base 16) extends that with the letters
a–f to represent values 10–15:
| Hex digit | Decimal value |
|---|---|
0–9 | 0–9 |
a | 10 |
b | 11 |
c | 12 |
d | 13 |
e | 14 |
f | 15 |
So the UUID 550e8400-e29b-41d4-a716-446655440000 is the hex number
550e8400e29b41d4a716446655440000, which in decimal is:
113059749145936325402354257176981405696 That's a very large number, but it's still just a number. You can do arithmetic on it, compare it, sort it — whatever you want.
The anatomy of a version 4 UUID
Not all 128 bits are random. A few bits are reserved to encode metadata. In the most common variant (version 4, the random kind), the structure looks like this:
550e8400-e29b-41d4-a716-446655440000
^ ^
| |
| variant (bits 64-65): always 0b10
version (bits 48-51): always 0b0100 (4)
The 4 in the third group tells you it's version 4. The leading
a (binary 1010) of the fourth group starts
with 10, marking the RFC 4122 / RFC 9562 variant.
Everything else is random. That leaves 122 bits of randomness, which
gives you about 5.3 × 1036 possible UUIDs — plenty
to avoid collisions in practice.
Converting between representations
Because a UUID is just a number, converting between the string form and an integer is straightforward. In Python:
import uuid
u = uuid.UUID("550e8400-e29b-41d4-a716-446655440000")
# UUID to integer
print(u.int)
# 113059749145936325402354257176981405696
# Integer back to UUID
print(uuid.UUID(int=u.int))
# 550e8400-e29b-41d4-a716-446655440000 You can also get the raw 16 bytes:
print(u.bytes)
# b'U\x0e\x84\x00\xe2\x9bA\xd4\xa7\x16DfUD\x00\x00'
print(len(u.bytes))
# 16 The string, the integer, and the byte sequence are all the same value — just different ways of writing it down.
Why does this matter?
Understanding that UUIDs are just numbers clears up a lot of practical questions:
- Storage: You can store a UUID as 16 bytes instead of a
36-character string. Databases like PostgreSQL have a native
uuidtype that does exactly this. - Sorting: UUIDs are comparable as integers. Newer UUID versions (v6, v7) put a timestamp in the most significant bits so that sorting by the UUID also sorts by creation time.
- Alternative encodings: If base 16 feels wasteful, you can encode the same 128-bit number in base 32, base 62, or base 64 for a shorter string. The underlying value doesn't change.
- No magic: The hyphens, the hex digits, the version nibble — it's all just convention on top of a plain number.
Next time you see a UUID, remember: strip the dashes, and you're looking at a 32-digit hexadecimal (base 16) number. That's all there is to it.