In Defence of Boilerplate
Better the repeated code you know.
Yesterday I finished a discussion about “generic” code. The source of the discussion was a Vue component that rendered variations on a “standard tabular view”. However it was becoming increasingly apparent that every time this component was used, we ended up having to add a new set of use-specific options.
Abstracting complex problems into simple components which can be reused is at the heart of programming. It means we don’t have to keep solving the same bugs, and often saves the next developer significant boilerplate.
“Don’t repeat yourself” is a mantra I find myself channeling often. But recently I’ve found myself cursing the code where I took it to heart.
To me there are two challenges. The first is an age-old problem of any implementation; it never seems to do exactly what the next person to use it needs. Even if you don’t build the abstraction until you’ve done it the long way round three times (which is always a good rule of thumb), you can bet than in a month someone will just need some specific tweaks; and before long you’ve got a Frankenstein of an interface.
But the more pervasive issue is that abstractions increase cognitive load. These days developers have to hold so much in their head; typically there’s a half dozen frameworks and core libraries behind any application. Every “time-saving” all-in-one component just adds another thing that the developer must (re-)learn to be productive.
Having optimistically made the journey to ‘one-component-to-rule-them-all’, I’m now beating a hasty retreat to tiny, composable, components. And where possible these components should be analogous with existing patterns. In our case, we’re exploding our “all-in-one” table back into smaller components which mirror their html counterparts (table, header, body, row, cell).
By sacrificing our single component with (what feels like) 100 codebase specific options into 5 “html-like” components, we gain two benefits. First, we no longer need to revise a specialist interface every time you want to use a table, because it is an analogue for a much more common framework we already need to have in our head (html).
But more interestingly it becomes significantly more extendable. Suddenly we don’t have to bolt on (and justify) a new option for each table feature. We can simply hi-jack or replace the building block components with our own specialist ones.
This journey has been a reminder that keeping your functions trimmed down to a single responsibility is only part of the story. It is also important to make sure that single responsibility is a tight one.
Of course, the fear that comes with a host of smaller components is that you’ve (re-)introduced boilerplate. Suddenly developers will have to repeat themselves! This experience has taught me, however, that time writing common boilerplate is not saved if you end up having to re-read the ‘time-saving’ code and documentation each time you want to use it.
The lesson: Don’t underestimate the cognitive load of replacing a known pattern with an ‘all-in-one’ factory; sometimes it is ok to repeat your self.