Redundancy is one of the biggest sources of defects in programming. Boilerplate is highly redundant. While it may be easy to implement, and often necessary, it reduces code legibility and increases maintenance costs. Boilerplate is a strong indication of a language or framework that is missing features.
Copy-and-paste in disguise
Copy-and-pasting code within a project, or even company, is a generally the undesired solution. It creates code duplication. It increase the chances of defects since the copied code has less review than original code. It then multiplies those defects as the code is copied again.
Boilerplate is a form of copy-and-paste. Even if we aren’t copying exactly, using code as a template to mindlessly produce new code is essentially the same as copy-and-paste. I’m a bit more forgiving of this practice though since often it is unavoidable.
Composition
A common occurrence of boilerplate is the composition pattern. Few languages offer a way to do this succinctly. The typical pattern looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class foo_impl : public common_interface { //the composed class common_impl core; public: int common_one( int a, float b, variant c ) { return core.common_one( a, c, b ); } int common_two( string a ) { return core.common_two( a ); } void common_three( custom c, other q ) { return core.common_three( c ); } } |
A class implements an interface and then uses a common implementation for most of the functions, or often for all of the functions. The idea is solid; I like composition in object oriented programming. The implementation however leaves a lot to be desired.
The first problem is defects. It’s quite easy to get the parameters in the wrong order when forwarding the functions. In a statically typed language the compiler will likely find most of those problems, but a dynamic language cannot. Neither language type can actually prevent us from calling the wrong function, though perhaps a statically typed one might give us warnings about unused parameters.
Did you even notice that I made three mistakes in my forwarding?
The second problem is one of clarity. Other than the comment “the composed class” I can’t quickly determine what structure this actually is. Instead of something indicating it is a “delegate”, I just see a bunch of individual functions calling other functions. It lacks significant semantic clarity.
The problem of composition is so common that many languages actually have solutions for it now. Things like traits and mixins, as well as explicit delegation extensions. I think those are all great features and it’s very unfortunate not all languages, even some very popular ones, don’t have a clean solution for this.
Skeletons in the closet
A lot of projects, or modules, are bootstrapped with standard code. We can follow a series of steps in constructing the basic code. Sometimes we get “lucky” and have a program that generates it for us. This type of skeleton code is common for web projects.
This type of activity makes me queesy. Before I can even start with the real part of our project I’m forced to work through a lot of boilerplate, redoing the work, and refixing the mistakes, that hundreds of others have already done.
I’m also left wondering whether I have to do this way, or it’s just a recommended example. I’ve been criticized before by colleagues for changing the standard setup. If the code is actually so critical then why exactly am I being forced to do it myself?
I would much prefer if library vendors figured out why they need all this code and then hide it via abstractions. An ideal platform would allow us to program just the parts interesting to our project without needing any boilerplate.
I’ve found bugs in skeleton code before; do all the projects out there thus have the same bugs in it? Even if the library author fixes the skeleton code all those projects will still have that bug in it. It’s a significant problem with boilerplate code in general: bugs in the primordial form get multiplied into every place it was used.
Hey, but “Patterns”!
This is not a criticism of patterns in general, but about how they are often implemented. Core concepts like visitors and adapters are sound. It’s often a problem of the languages we are using. They have no way to concisely express a pattern; we resort to boilerplate redundancy to workaround a language limitation.
It’s depressing that reducing redundancy is not a key focus of most programming languages. It’s perhaps the biggest source of defects and maintenance cost. Being able to wrap up a pattern, give it a name, and share it’s logic, significantly improves code legibility and quality. Anytime you can do this, please do. Anytime you can’t, then post examples to your favourite language forum. Maybe somebody can help, or maybe with enough interest a new language feature can be introduced.
We need to stop accepting boilerplate code!