As creators of
Premature use of Patterns
Patterns is a great way to capture a concept in a general description which can then be used over and over again. The gang of four presented a big collection of patterns in 1994. These have been used widely since, and a lot of them have either been incorporated into the technical vocabulary, or even natively into programming languages. While patterns are great at providing a collection of well thought out solutions for common problems, they are also easy to overuse. Patterns often comes with abstractions, which in turn increases complexity. If a patterns is applied to a system that is already complex, it is important to consider if the complexity added is worth the advantage the pattern gives.
Most patterns are meant for scaling applications. The pattern introduces some abstraction that makes it easy to add functionality without changing too much code. That is also why patterns might be a bigger burden than relief if you do not end up utilizing these facilities. A factory that can produce two variants or a builder with two builder methods might be more complex to use, than if the same had been implemented without the abstractions.
This brings me to YAGNI, a concept from Extreme Programming, and in the linked article explained by Martin Fowler. It is an acronym for “You aren’t gonna need it”, which is exactly the risk of overusing patterns. Spending the time to add a pattern to the system will both incur a cost on understanding the system, and on delivering the features currently in the pipeline. Furthermore, if the pattern is never used to its full potential, or even worse, it turns out that it does not solve the problem properly, it is either wasted effort or it might even have to be completely rewritten. Thus it is very important to consider if a pattern will pay off immediately, or only after it is being utilized in a couple of instances more. If the latter is the case, it might very well not be the right time to add the pattern.
Anticipating the future
When building a new feature we often get told that this feature will be needed for more instances of variants of the same problem. The business has a plan of selling this feature to this and that customer, and they tell you that they will be similar. And then begins the anticipation race. Knowing that the feature should be future proof, you begin thinking about how to make it usable for customers with all sorts of requirements. Little by little the feature becomes littered with hooks and class hierarchies that accommodate various changes that surely will come when the feature gets more use. Until it turns out that we anticipated the wrong requirements.
Once again YAGNI must be your mantra. By attempting to anticipate the future, you will end up with a much more complicated structure than needed, which will end up hindering you when you want to do changes for any other reason. The fact is that we can never anticipate the future, and starting out simple and adding to it, is always easier than starting with a complex structure and modifying that to fit a new case that is not exactly what was anticipated.
Instead of adding complexity in anticipation, it is much more valuable to start out simple and get the customer to actually use it. Looking at real usage you can gain information about what needs improvement and how the feature is actually used. When a customer arrives that needs a slight variation of the feature, you can actually put him in front of the production version which will help to gather the exact requirements.
Overhead throughout the cycle
Looking further down the pipeline of development, adding unneeded complexity adds overhead throughout, which means that the cost of unneeded complexity is amplified many times in its lifetime.
The reviewer who has to review the changes will need to spend more time understanding the code. The additional complexity and abstractions will be a source of confusion and further add to the time spent getting the actual necessary feature delivered.
When the code arrives in production it will need to be maintained, and any bugs found in the code will take longer to debug because of the added complexity.
Conclusion
Finding the right level of complexity is difficult, but simplicity will a lot of time be an advantage. It makes the whole pipeline smoother, and when the feature is actually in production, you have the opportunity to investigate what the customers want and you can make a justified decision on the right abstractions to use in the future. Manage your complexity or gain perplexity!