I spent some time last year (year before? who knows, it’s covid) refactoring Excalidraw. A friend, Christopher Chedeau, wrote it to give a hand-written look to drawings. Another friend, Pete Hunt, added support for distributed drawing. Others jumped in and added their favorite features.
The functionality was impressive. Usage was growing like crazy. The code was not to my taste. Excellent—they made it run, I’ll make it right. What a great way for me to practice big changes in small safe steps.
I nibbled away at the code, one little diff at a time. I made things slightly more cohesive by moving bits of code that changed in the same PRs so they were adjacent. Doing this showed me that App.tsx was doing several things at once—application lifecycle, swapping changes with remote drawings, handling user interface events.
I decided I’d extract all the distributed editing logic in one place. I called the object Portal because it spanned between worlds. First I moved distribution-related state into Portal. Then I started moving bits of logic that only used Portal state into Portal. Some was easy to move. Other logic mixed distributed editing with other concerns so I just left it.
Then one day I fetched a new version and discovered that someone had moved all the logic from Portal back into App, leaving only the state. WTF?
WTF was that I’m a splitter and I was working with lumpers. There’s a tradeoff in design between:
Elements with more, simpler sub-elements and simpler relationships between those sub-elements. App had many sub-elements. They were simple, just functions and state. Many didn’t refer to each other at all. But it was all there in one place if you wanted to find anything.
Elements with fewer, richer sub-elements and richer relationships. My Portal bundled together 3-4 pieces of state and App’s relationship to it was getting richer as I added methods with intention revealing names to Portal, replacing blobs of code.
Lumpers like everything in one place. They’ll take the hit on cohesion, with all that implies, in order to not have to reason across elements.
Splitters like lots of little elements with rich relationships. They’ll take the hit on coupling from time to time in order to not have to think about too much at once.
I’m a splitter. I’ve never been able to deal with much complexity at once so I developed thinking tools for partitioning complexity instead. If my audience is lumpers, though, it behooves me to speak their language.
My theory, with absolutely no evidence beyond intuition, is that lumpers have had bad experiences with poor naming, that required jumping all over the code to see what was happening. Now they want to see all the code in one place because they don't trust code that they can't see. Of course this is a good way to justify my splitter habits.
I have absolutely had bad experiences with poor naming and it's made me more of a lumper. And bad experiences with bad tools, *also* making me more of a lumper. Give me a big text file with a high percentage of all relevant code and I don't *need* to trust names or tools, so I don't need to worry whether they'll work.
In some sense that means a high-trust environment "should" be better. With good names and good tools, surely I shouldn't need to verify. But it's not just bad naming and bad tools. There are also subtle bad interactions of other kinds, and the fact that humans (reasonably, understandably) tend to read a thing's name instead of its definition and not see "this doesn't do what it says it does" bugs.
Splitting is great, assuming a long list of things that may or may not be true. And one of my least-favourite things in the world is arguing about whether a tool is good enough I should always trust it, followed by a long and bitter list of "yes, well, okay, it caused that problem for *you*, but it doesn't do that *in general*." Ah, Eclipse, I do not miss you even a tiny bit.