Clean Code: Four Simple Design Rules - Obligatory Read
I consider the Clean Code book to be obligatory for every programmer. And if you currently haven't the time to read it all at once then you should read - and take deep into your heart - at least the 12th chapter Emergence by Jeff Langr, which introduces Kent Beck's Four Simple Design Rules and explains thoroughly what they mean and why they're so important. The rules, in the order of importance, are:
Runs all the tests - Why is it so important to have comprehensive tests1, which are run and pass all the time, as this rule implies? Foremost because to be actually able to write unit tests, the code must follow the well-known high-quality design principles such as simplicity, single responsibility (including dependency injection), low cohesion etc. - only small, on each other and on the environment maximally independent objects are easy to test.
Contains no duplications - "Duplication ... represents additional work, additional risk, and additional unnecessary complexity." It can have many forms - lines of code that are same (or similar - adepts for generalization), duplication of implementation (e.g. if a collection has int size() and bool isEmpty(), isEmpty shall be based on size() == 0 to reuse the implementation) and other ones. You usually apply abstractions such as Template Method to remove duplication. The true value is in how it leads to the reduction of complexity.
Minimizes the number of classes and methods - The least important yet absolutely not negligible rule:
Please realize that this rule doesn't suggest you should have only few classes and methods - the 4 rules are ordered by priority and the last one only applies if the previous ones are satisfied. As you certainly noticed, e.g. rule #3 requires "keeping your functions and classes small" and thus also their counts high. But it's also possible to have too many classes and methods and rule #4 is here to remind you of that and to help you find the right balance. Small stuff is easier to understand - but if there is too many (though perfectly understandable) pieces, you won't be able to see the whole picture. To be concrete: I've seen code base where every method was in a class of its own. Wouldn't you agree it's too many?
The fourth rule is sometimes alternatively stated as "Has no superfluous parts", meaning that you should remove all classes and methods that you don't really need to get rid of unnecessary complexity and maintenance overhead. I'd suggest reading the discussion about what "minimize the number of methods/classes" really means. But I feel that this new phrasing isn't an alternative but rather an addition to or a clarification of the original one for you should be really aware when optimally many classes/methods become too many.
I recommend the Clean Code book and especially chapter 12 to every programmer but I'm actually not really sure when it's the right time for a junior programmer to read it for I can imagine that for somebody without sound practical experience both with a beautiful, clean code and with a terrible, low-quality one, the guidelines in the book are way too abstract (however concrete they're) and difficult to grasp and appreciate - and their appreciation is perhaps the important factor for being motivated and able to put them into practice. But it certainly won't harm to read it as early as possible and come back to it now and then to discover new depths and resonance with one's recent experience.
Experience and craftmanship is also necessary to be able to apply the clean code principles and guidelines correctly and effectively because, to some extent, one rule always conflicts with another one and you must have some sensibility to the code to be able to find the proper balance between them and to avoid any trace of "pointless dogmatism".
Finally, I'd like to thank all those who help(ed) me understand that - and why - my code is bad and thus lead me towards better quality. Please keep on! I'd like to also thank Rober Pirsig for pointing out the importance of quality to me :-)
Update: You may also want to check my blog Hidden Dependencies Are Evil – Arguing With The Clean Code (Slightly), which deals with Clean Code’s chatper 14: Successive Refinement.
- Runs all the tests
- Contains no duplications
- Expresses the intent of the programmers
- Minimizes the number of classes and methods (this isn't as controversial as it may sound, see below)
Runs all the tests - Why is it so important to have comprehensive tests1, which are run and pass all the time, as this rule implies? Foremost because to be actually able to write unit tests, the code must follow the well-known high-quality design principles such as simplicity, single responsibility (including dependency injection), low cohesion etc. - only small, on each other and on the environment maximally independent objects are easy to test.
Remarkably, following a simple and obvious rule that says we need to have tests and run them continuously impacts our system's adherence to the primary OO goals of low coupling and high cohesion. Writing tests leads to better designs.This is no theoretical statement - I've personally experienced it many times. See e.g. In Test Driven Development, how do unit tests help drive good design? (answer: they don't - but they make bad design so painful that you will want to change it).
Contains no duplications - "Duplication ... represents additional work, additional risk, and additional unnecessary complexity." It can have many forms - lines of code that are same (or similar - adepts for generalization), duplication of implementation (e.g. if a collection has int size() and bool isEmpty(), isEmpty shall be based on size() == 0 to reuse the implementation) and other ones. You usually apply abstractions such as Template Method to remove duplication. The true value is in how it leads to the reduction of complexity.
As we extract commonality at this very tiny level, we start to recognize violations of SRP. So we might move a newly extracted method to another class. That elevates its visibility. Someone else [...] may further abstract the new method and reuse it in a different context. This "reuse in the small" can cause system complexity to shrink dramatically. Understanding how to achieve reuse in the small is essential to achieving reuse in the large. [Emphasis by JH.]Expresses the intent of the programmers - Writing code that expresses well the intent of the programmer so that another person can easily understand it is important for many reasons, especially taking into account that "the majority of the cost of a software project is in long-term maintenance". Expressiveness is also one of the essential requirements of a clean code.
The clearer the author can make the code, the less time others will have to spend understanding it. This will reduce defects and shrink the cost of maintenance. You can express yourself by choosing good names. We want to be able to hear a class or function name and not be surprised when we discover its responsibilities.I'd like to add here that the XP motto "the code is the documentation" doesn't mean, as some people read it, that you simply don't bother with any comments at all. This is not an elimination of some work, it's actually a challenging commitment to writing code so, that it reads as documentation - which essentially means that it is maximally expressive. And writing such code is much, much more difficult than typing whatever comes on your mind and then adding few comments here and there. Regarding comments and their replacement by expressive method names, the Clean Code book has a whole, highly inspiration chapter on that. (I have to mention a nice post on why code isn't documentation, which I've quite enjoyed.)
You can also express yourself by keeping your functions and classes small. Small classes and functions are usually easy to name, easy to write, and easy to understand.
You can also express yourself by using standard nomenclature. Design patterns, for example, ... .
Well-written unit tests are also expressive. A primary goal of tests is to act as documentation by example. Someone reading our tests should be able to get a quick understanding of what a class is all about.
But the most important way to be expressive is to try. All too often we get our code working and then move on ... without giving sufficient thought to making that code easy for the next person to read. [Emphasis by JH.]
Minimizes the number of classes and methods - The least important yet absolutely not negligible rule:
Even concepts as fundamental as elimination of duplication, code expressiveness, and the SRP can be taken too far. In an effort to make our classes and methods small, we might create to many tiny classes and methods. So this rule suggests that we also keep our function and class counts low.I like the mention of "pointless dogmatism". As too often in religion, pointless dogmatism is a sign of an insufficient, shallow understanding of a principle, its true purpose and the proper context of application. It requires experience, open mind, and reason to apply principles correctly. (By no means want I claim that I have that experience and bright reason :-).)
High class and method counts are sometimes the result of pointless dogmatism. ...
Please realize that this rule doesn't suggest you should have only few classes and methods - the 4 rules are ordered by priority and the last one only applies if the previous ones are satisfied. As you certainly noticed, e.g. rule #3 requires "keeping your functions and classes small" and thus also their counts high. But it's also possible to have too many classes and methods and rule #4 is here to remind you of that and to help you find the right balance. Small stuff is easier to understand - but if there is too many (though perfectly understandable) pieces, you won't be able to see the whole picture. To be concrete: I've seen code base where every method was in a class of its own. Wouldn't you agree it's too many?
The fourth rule is sometimes alternatively stated as "Has no superfluous parts", meaning that you should remove all classes and methods that you don't really need to get rid of unnecessary complexity and maintenance overhead. I'd suggest reading the discussion about what "minimize the number of methods/classes" really means. But I feel that this new phrasing isn't an alternative but rather an addition to or a clarification of the original one for you should be really aware when optimally many classes/methods become too many.
Notes
- "Comprehensive tests" of course doesn't necessarily mean 100% coverage, which is at least sometimes both impossible and useless (think simple getters/setters, GUI). You have to find the right coverage for yourself (though there are arguments that 100% coverage is the ideal goal).
Conclusion
Studying, understanding, and finally applying the rules described above makes it possible to create much better (micro)designs and code and thus quality. As it is the case with Zen (as I've been told), the basic truths are very simple, but to understand them and to truly master them it takes years and a lot of effort. The important thing is, as the author himself mentions, to try. (I'm myself only somewhere at the beginning of the path. You'll know I'm there once you see me levitating above my notebook.)I recommend the Clean Code book and especially chapter 12 to every programmer but I'm actually not really sure when it's the right time for a junior programmer to read it for I can imagine that for somebody without sound practical experience both with a beautiful, clean code and with a terrible, low-quality one, the guidelines in the book are way too abstract (however concrete they're) and difficult to grasp and appreciate - and their appreciation is perhaps the important factor for being motivated and able to put them into practice. But it certainly won't harm to read it as early as possible and come back to it now and then to discover new depths and resonance with one's recent experience.
Experience and craftmanship is also necessary to be able to apply the clean code principles and guidelines correctly and effectively because, to some extent, one rule always conflicts with another one and you must have some sensibility to the code to be able to find the proper balance between them and to avoid any trace of "pointless dogmatism".
Finally, I'd like to thank all those who help(ed) me understand that - and why - my code is bad and thus lead me towards better quality. Please keep on! I'd like to also thank Rober Pirsig for pointing out the importance of quality to me :-)
Update: You may also want to check my blog Hidden Dependencies Are Evil – Arguing With The Clean Code (Slightly), which deals with Clean Code’s chatper 14: Successive Refinement.
Related
- Jeff Langr's own summary of the 4 rules
- Current Xp Simplicity Rules and Older Wording Of Xp Simplicity Rules (still useful to read, I think)