The “Zero, One, Infinity” rule

A simple yet powerful rule of thumb when designing software

One Ring — by DRP

Here is the first paragraph from Wikipedia on what is the “zero, one, infinity rule” (ZOI):

[ZOI] is a rule of thumb in software design proposed by early computing pioneer Willem van der Poel. It argues that arbitrary limits on the number of instances of a particular type of data or structure should not be allowed. Specifically, an entity should either be forbidden entirely, only one should be allowed, or any number of them should be allowed.
en.wikipedia.org/wiki/Zero_one_infinity_rule

In other words, if you consider a data structure, either it doesn’t exist, or there is only one of it, or it is unlimited. One common example is a folder structure. There is precisely one root folder, and you can add an infinite amount of folders.

This rule intends to fight arbitrary limitations in codebases that would have, for example, something like MAX_FOLDERS_NUMBER=10000

— 😐 But sometimes we do need to limit data structures?

Correct but I’m using this rule to describe something else. This rule, can also help us remember a key concept in design — Single Responsibility Principle (SRP). If we write a component that has some responsibility, either it is the only one doing it, or there will be infinite* different implementations with a similar responsibility.

*Obviously not really infinite but if I have, for example, 50 different functions doing mostly the same thing I cannot stand a chance of make sense of which is doing what.

— 🧐 Can you offer an example?

Sure! Here is a simple example to outline what I mean:

Let’s say you have an Orders microservice that has a data access layer responsible for getting orders from the database.

At first, you might have a single method there:

class OrdersRepository{
getOrders(ids: number[]): Order[]{
//...
}
}

But then, over time, if you are not careful, you will end up with something like this:

class OrdersRepository{
getOrders(ids: number[]): Order[]{
//...
}
searchOrders(searchTerm: string): Order[]{
//...
}
getOrdersByParent(parentId: number): Order[]{
//...
}
getOrdersSortedBy(field: string, isDesc: boolean): Order[]{
//...
}
getOrdersTree(parentOrderId: number): Order[]{
//...
}
getOrdersNames(ids: number[]): string[]{
//...
}
// ...
// you get the picture
}

Now imagine a developer trying to add a feature that has a new use case for getting orders. Would that developer find the correct method to change? (if there is even one that makes sense) or will they add a new one?

— 🤔 How does the ZOI rule apply here?

There should only be one getOrders method, or you’ll end up with infinite such methods over time.

— 🤨 But what about the SOLID principles? won’t we want it to be extended?

Good question! In this particular case, we could create another class/function responsible for building the query options, and the single getOrders method will get the result of that builder. That way, we maintain the SRP and Open-Closed (the option builder is open for extensions). If we introduce interfaces, we get pretty much all the remaining letters of SOLID

interface QueryBuilder{
build(): any
}
interface Repository<T>{
getItems(queryBuilder: QueryBuilder): T[]
}

Conclusion

Remember the ZOI rule if you don’t want your codebase to turn into spaghetti. If someone asks you why not add just one more component that does the same thing, just slightly different, tell them two implementations are the first step to having infinite implementations.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store