A heuristic for deciding how many classes, functions, methods, modules, etc. you should add to your system and whether you should add one more.
The Heuristic
Lots of things is almost never a problem. Lots of connections between things is almost always a problem.
One of the hurdles that people sometimes encounter when learning to design simple systems is that simple systems tend to be made out of pieces that are much smaller, so we tend to need a lot more of them.
Sometimes the concern is that too many functions/classes/files will make it more difficult to maintain because it’s easier to keep track of 50 files than it is to navigate 200 files.
Other times, people make the mistake of conflating “fewer interconnections between artifacts” with “fewer programming artifacts”. Simple means one fold not one thing—it’s referring to the relationship between the parts. The naive approach to reducing the interleaving between parts is to reduce the number of parts, because, hey, if there are only three classes in my entire program it’s pretty hard to get them tangled together, right? Guys?
We can’t get too fixated with “one”. In particular, simple doesn’t mean that there is only one of them. It also doesn’t mean an interface that only has one operation. So it’s important to distinguish cardinality—counting things—from interleaving. What matters for simplicity is that there is no interleaving, not that there’s only one thing. That’s very important.
Rich Hickey, Simple Made Easy [00:04:00]
Directories allow us to create trees to search our files and we know that searching trees is O(log n), which means each additional file actually adds less additional cognitive burden than the last file. Yes, a system with 200 files is technically more difficult to navigate than a system with 100 files, but it’s logarithmically more difficult and certainly not twice as difficult.
And I have to ask, does having thousands of lines of code inside a file really make it easier to find what you’re looking for?
So why do people continue to design systems with large pieces? Sometimes the pieces are just complicated and splitting it up actually makes it harder to understand. Jonathan Blow says he often prefers long C++ functions with inline blocks for that reason. That’s a good reason to design something that is big.
I suspect that most of the time people design big parts because they don’t have good heuristics to know when and why to spilt something into a separate piece. They may not know how to organize an entire system from such a large number of tiny pieces.
I once designed some code that used over fifty classes all for a single REST endpoint. When it was done, I showed it to one of the senior guys and he instantly understood it. Then he gave it to one of the most junior developers on the team and that guy read it. This was a guy who couldn’t do even the most basic tasks without supervision. The junior guy came back after twenty minutes and we asked if he had any questions and he said no, he understood all of it. He didn’t know how to design code like that, but he understood how it worked and knew exactly where to go to make certain changes.
Using lots of classes made that code easier to understand, not harder.