Embrace flatness
There's a surprisingly common pattern in programming and just generally in life. Flat structure seems better than hierarchy.
Matklad wrote a great article about structuring Rust workspaces practically: just use a flat structure. No nesting, all crates live in a single directory. I like this approach and many open-source projects adopted it since.
There's a great insight in that article
Tree structure tends to deteriorate over time, while flat structure doesn't need maintenance.
After internalizing this, I started to see this pattern in many different contexts, even outside of programming, just generally in organizing stuff. Some examples:
-
Any kind of file organization
When browsing, do you like to browse a bunch of different subdirectories, or just a single one?
-
Context menus in programs
How fun is deep nesting when looking for a feature?
-
Organizing people
Many people observed that organizational structures are worse when deeper. As you add more management layers, you get a structure in which people suffer (lookup immoral mazes). People tend to be happier in flatter structures. My experience confirms this.
-
Many, many, many programming problems
When introducing a tree structure into almost any problem, everything gets more complicated. Flat structure has a few relatively simple linear algorithms. Tree structures have orders of magnitude more complex algorithms with various variants and trade-offs. There are many more options to choose from and a lot of potential pitfalls.
-
Home organization
We tend to have very few layers of nesting when organizing things. Once you open a drawer, you can usually see everything that's inside it. A path to every item in your house is probably almost always 1-2 layers. We like when most things are easily reachable. If we put stuff deeper in the tree - say in a separate room, in a cabinet, in a drawer, in a little box inside that drawer, we are less likely to use it.
-
Software dependencies
Deep dependency chains are worse than flat ones. If a library you depend on has a problem, it's easier to fix than if that library has a problem because its dependency has a problem, and so on. The cost of depth is very high. I've spent countless hours dealing with these problems in the frontend world.
-
Java package structures
Do I need to say more?
The cost of a layer 🔗
Introducing a hierarchy increases complexity and often multiplies the number of issues and choices you have to manage.
Of course, we can't avoid using hierarchies and trees; it's just that flatter often means better. How much? It depends, but my rule of thumb is that the cost of a layer is roughly a hundred times higher than the cost of an item in a layer. The Cost of depth is 100 times the cost of breadth.
Think about how you organize files. How many photos in a single directory is too much? Probably a lot. You don't want to switch directory for every other photo, you want to do that very rarely. A hundred photos per directory seems reasonable.
The cost of a layer depends on the relationship between parent and children. If there is some very clear unifying principle (e.g., photos from Christmas 2021), the cost is lower. If there are other relationships between items (e.g., all photos of our daughter), the cost is higher because the layer puts a barrier to the second relationship.
Do you want to put all yearly Christmas folders into one folder with all Christmas? Well, probably not, because you could also put all folders from a single year to one folder and neither of these relationships is strong enough differentiator to be worth the indirection.
Very often there are multiple hierarchies involved in the same problem. Flat structure is often better, because it doesn't pessimize any of them. This is also mentioned in the Matklad's article.
Takeaway 🔗
If you see a tree structure in a problem, try to find out if there's some flat alternative that you could use instead. Try to find out if you can transform the tree problem into a flat problem. Just because you see the hierarchy doesn't mean you should base the solution on it.
For example, to implement a simple file system, it'd be very tempting to store its state as a tree data structure, because that's how we think about file systems. But if you instead store it as a single flat table with (path, data) columns, your problem suddenly becomes much simpler. Even though the hierarchy is still there, it's just encoded in a different way - in paths (I think I stole this idea from some other blog post, but I couldn't find it).
Think about how would you implement very basic file search with both approaches. Using the tree structure would involve some recursive tree walking algorithm with many edge cases, while flat structure could simply be a linear scan through the table. Or you can sort the data and use binary search, which is hierarchical, but doesn't require us to store the data like that.