Beyond UUIDs: Exploring the Advantages of ULIDs in Modern Software Systems
Most systems have moved on from using basic sequential IDs to using UUIDs (Universally Unique IDentifiers), with whom they achieve universal uniqueness across the whole system, because the probability of collision between two UUIDs is so minuscule that we can say it’s practically 0.
Here is a very good article on the uniqueness of UUIDs. Definitely worth your time, in my opinion!
Another benefit that these IDs provide is their unpredictability. In a RESTful API scenario, where we attach IDs to the URL, it would be impossible for an ‘outside’ user to guess the previous or next id of a customer in our system:
Using sequential IDs (easy guess of prev user in the system: 15–1=14 🤷♂️): https://demo.com/api/product/15
Using UUIDs (no way we’re guessing the prev or next user in our system): https://demo.com/api/product/15d905dc-74e4–41b2-a014-bced748140c3
Now, what if I told you that there is a new cowboy in town going by the name of ULID (Universally unique Lexicographically sortable IDentifier), who offers even more advantages to us. 🫢
Why should we consider ULIDs?
ULIDs offer everything that UUIDs offer, but in addition:
- they are sortable. Having a time component embedded within them, ULIDs can be sorted in the order of their generation.
- they have compact string representation: ULIDs are typically represented as a 26-character string, which is shorter than the standard 36-character representation of UUIDs. (thus saving storage space and offering more efficiency when working with database or transmitting data over the network)
The structure of a ULID
ULID consists of two main components:
- timestamp portion (48 bits)
- randomly generated portion (80 bits)
.. which are then combined into one, forming the 26-character and 128-bit compatible ID.
Here is the structure of a ULID:
01H0SJZ46B70QS1MBPRZP37MP3
The first 10 characters are the timestamp portion: 01H0SJZ46B.
The remaining 16 chars are (by default) randomly generated with CSPRNG (Cryptographically Secure Pseudorandom Number Generator): 70QS1MBPRZP37MP3
🔗 What do we gain from the sortability of ULIDs?
The main gain, in my opinion, is simplified indexing. Sorting ULIDs simplifies the process of indexing and querying data by creation time.
By utilising ULIDs as a part of the primary key or indexed field in databases or search engines, we can achieve efficient sorting of records based on their creation order. (removing the need to explicitly add a created_on field to the DB table 🎉)
By sorting ULIDs in ascending order, we can retrieve data in the order it was created. This removes the need to explicitly add a created_on field to our table and writing more extensive database queries.
🔐 ULIDs and security
ULID takes a different approach compared to many random ID generators that, for example in JavaScript, rely on the potentially unsafe Math.random()
function. ULID addresses this concern by disabling the usage of Math.random()
by default.
Instead, ULID automatically selects an appropriate random number generator based on the specific situation or context in which it is being used. But by default, it uses CSPRNGs (system-provided or platform-specific ones, coming from external libraries for that purpose)
ULIDs can also leverage system randomness or configurable options as tools for generating random characters.
💡 Specs, libraries, more info and references about ULIDs
- You can find all the spec about ULIDs here.
- To use ULIDs in your projects, you can leverage one of these libraries, depending on the language: ulid-java (for Java); ulid (for JS); oklog/ulid (for Go); python-ulid (for Python)
- 📹 This video, motivated me to dig deeper into the topic of using ULIDs in stead of UUIDs. Hussein basically explains how Shopify improved their database writes by 50%, simply by switching to ULIDs! 😱
- Here is the Shopify blog post, which hinted us that they’re using this type of IDs.
- A simple ULID generation tool, like the one you probably already use for UUID v4s. 😁
👋 And that’s a wrap! This is the end of our exploration into UUIDs vs. ULIDs.
🤓 Hope that you’ve gained fresh perspectives and insights on why ULIDs might be the better choice in certain scenarios.
🛠 I have faith that this article sparked at least a little of your curiosity and inspired you to consider ULIDs as a valuable addition to your tech toolkit!