"Clean" Code, Horrible Performance?
Accidentally I found this video named: “"Clean" Code, Horrible Performance”. In this video the author explains that almost all postulates of the so-called “Clean Code” are wrong because they negatively affect performance (up to 25 times in his video), and he finishes the video with words: “You should never actually do them”.
Well, the author is right that all techniques explained in “Clean Code” will negatively affect performance. The same as writing code in C++ will make your code slower compared to assembler.
But there are a lot of reasons why most developers switched to languages like C even though their apps became slower and bigger. One of those reasons is portability. For example, there were several examples of really fast 16-bit applications that were written in assembler. But when the world was transitioning to 32 bits they had to rewrite pretty much everything from the beginning. And they failed to do so. But companies who opted to slow C just recompiled their applications, fixed a few bugs, and suddenly their application became faster because the new hardware was faster.
The second reason is the speed of development. Using any high-end language any developer can work much faster. And not only because the compiler can save a lot of time by generating a lot of boilerplate code but also because the compiler can help a lot with the logical validation of the application.
But the most important reason is that it is much easier to refactor an application. You see one of the most important features of software development is that there is no end. When engineers finished building let’s say bridge they moved to different and new projects. But in software development, very often there is no end. The same application changes over and over. And refactorability is the most important feature of modern code. If the code is hard to refactor then it is bad code, period.
I’m always saying that writing code costs nothing. All expenses are in refactoring of that code. Unrefactorable code will lead to code duplication because nobody is willing to change old unrefactorable code and accidently break something. So developers start to build parallel structures that make code slow, fragile, and unsupportable. I have seen it all many times with many layers of parallel structures built on top of each other. Imagine a bunch of tangled ropes. It is much easier to keep them untangled than untangle them after they turned into a blob of mess.
But let's see a practical example. Imagine we need to add let’s say oval to the list of shapes. A new developer may simply not be aware of which places they have to change. There could be many functions with a switch statement that uses something from a shape.
Now a new developer has to find all these places that use shape type and modify all of them to support oval. And guess what? That developer will definitely miss a few places and some functions will return incorrect values. In the worst case, results will be kind of random.
I know that there must be tests, etc., but a lot of companies are writing code without tests or do not use tests for every simple thing because tests also cost money to develop and support.
And because some functions don’t work correctly there will be a lot of bugs. Most of them will be found and fixed but some may stay in your code for years. And for years, your software will eventually crash frustrating your customers. It will cost you a lot of money and reputation.
I’ve seen it many times. Sometimes you may change completely unrelated code and some other code starts to crash and you definitely know that your code is not affecting that code. You can even put a breakpoint to your code to make sure it is never executed in this scenario. But as soon as you revert your code, everything will be back to normal. You will spend a lot of time fixing these kinds of errors.
Now let's compare it with a “Clean Code” way. A junior developer will be tasked to add an oval to the supported shapes. The developer will create a new class and inherit it from the Shape class. And code will stop compiling because the new class must implement abstract virtual functions. And after that junior developer will implement these functions everything else will just work!
Now imagine a different scenario. You want to refactor how oval works. For example, represent it with polygon. For the “Clean code,” it is very simple, all the code is in a single place and you can easily review it.
But with “fast code” you have to search all references and it will not guarantee that you will find something because there could be a default switch case label. Let's say a function that returns how many points that shape has. The switch may handle rectangles, squares, and triangles. For everything else return zero. So searching all references of the oval you may actually did not find all references.
And sometimes people had some common code let's say for the circle and oval. So it will be 2 case labels one under another. For example, for the oval and the circle, the code returns zero as the number of vertices. However, the developer may accidentally change the common code for the circle and oval assuming that they change only oval.
And guess what? Hardware is cheap. Imagine one developer who can develop slow but relatively bug-free code. Yes, code is not efficient but to write efficient code the company will need 2 developers.
For the first case, the company can buy 2 servers and have a “clean” code. For the second case, the company will need only one server and they will have I would say a mess that only these 2 developers can support. The first case is simply cheaper and better for all companies.
Moreover, it is fast and easy to convert a “Clean Code” code into performant code with the switch statement. But it is almost impossible to do the opposite conversion and it will take way more time and will introduce way more bugs.
Surely there are applications where performance is critical. For example computer games. But I saw the source code of Counter-Strike Global Offense and they more or less stick to the “Clean Code” paradigm. And yet their game is really fast. In fact it is a game with a crazy amount of FPS.
In conclusion, I would like to state that the author does not understand what problem people are solving with programming. Performance does not mean anything by itself. If I buy a calculator that can calculate any operation in 0.1 seconds it is enough for me. I don’t care if a calculator will do it for 0.01 seconds or less. I will not feel any difference. But I would definitely appreciate a cheaper calculator.
Performance is just one of the features of the program. Yes, in some cases, it may be critical but in most cases, stability, refactorability, and the development cost are much more important.
Clean code is not something that Martians brought upon us and forced us to obey. These principles help develop code that is bug-free, easy to support, and read. People spent around 50 years figuring out how to write programs in the most effective way and they came to these principles. Yes sometimes we have to violate them to write fast code, but most time people must stick to “Clean Code”.
I hope it helps someone.