When abstractions, design patterns, and design principles lead to spaghetti code
Published on 2019-05-26.
If you are thinking about abstractions, design patterns, and programming principles while you are programming, you are not really programming, rather you are wasting valuable time fantasizing. The end goal is always to have good working readable code, not code that has been bent, twisted, and shoved into formations and structures in order to fulfill the vanity of someone living in a fairy tail.
Some time ago I was going through some code of a very efficient web application. The code was written in Go with a single function that consisted of several function calls to other functions, some logging, some error handling, and some clean up that made sure that incoming requests was finalized properly. A very performant Go application.
Some time thereafter someone decided that the code needed refactoring, not because it was hard to read, and not because it wasn't performing well. No, because it didn't follow any of the famous design principles.
I know that the person who decided to refactor the code did so with good intentions. After all, he had followed every single principle and design pattern to the letter in his refactor. The problem however was, as so often is the case, that he had been following these so-called design principles so fanatically that the end result was a horrible mess.
The first thing he had done was to implement the single responsibility principle by cutting each part of the code up into tiny functions in separate files.
In order to do that and still make the code work he had to implement new functions that called previous functions that again called other functions.
The next step he did was to implement interfaces because according to his perceptions, responsibility has to be abstracted away. The end result was code that:
- No longer performed as well.
- Consisted of at least twice as much code.
- Could no longer be easily followed and understood.
- Took at least twice as much time to read, once you had tracked everything down and actually figured out what belonged to what.
Even if the end result would have been code that performed just as well as the original code, the refactor was bad, it was overengineering!
Code is a tool you use to solve problems. It's not something that has to be force-fed into principles in order to solve problems.
Programming principles are just guidelines and theory. Some are very good rules of thumb. But they are NOT something that should dictate how you solve your problems.
Consider these points:
- Focus on the problem as the main concern.
- Good code is never about the code, it's always about the problem.
- Use principles cautiously and only when they clearly make an improvement. Never implement a principle just for the sake of the principle.
- Understand and differentiate between really pragmatic principles and less useful things such as Design Patterns.
- There are no fixed set of rules that dictates how you need to solve a programming problem.
- Efficient code is very often quite contrary to code developed with a focus on principles.
Your code is worthless if it doesn't solve the problem efficiently. Efficient code requires much less hardware and power - and this is important. The general idea that hardware is cheap and you can just throw more hardware at inefficient code is irresponsible. It's like one person flying in a Boeing 747, alone, without any other passengers.
As software developers or engineers we need to keep our focus on the really important stuff, not on the academic theory and fanatical babbling of people who like to feel important because of their principles and dictations to others.