In this article, I want to discus clean-code as a modern approach to build elegant software systems, and to spot the light on the main rules, practices, behaviors and knowledge that any developer needs in order to write clean code. I have been influenced by Jeremy Clark's ideas that he spreads in his sessions on YouTube, where he sets many valuable practical advices along with clear practical guide.
Clean is a Rule
"Try and leave this world a little better than you found it." - Robert Baden-Powell Rule -
"Always leave the campground cleaner than you found it." - The Boy Scout Rule -
"Always leave the code cleaner than you found it." - Clean Coder Rule -
"Always check a module in cleaner than when you checked it out." - Clean Coder Rule -
The clean code, from Jeremy Clarck's view point, is the code that is readable, maintainable, testable and elegant. Some people like Jeremy, doesn't favor the title "architect" to be hold by software engineer, that is because software industry is so different than architecture industry, software has particular aspects which don't exist in other fields, for example bug fixes, business changing, enhancements and new functionalities. This specialty of the software industry needs developers to be careful to write clean code because of the known truths: (1)clean code saves time (2)Any code has at least five years life-span if not ten or more (3)We can't take a short term view of software.
What really prevents developer to write clean code is he usually says "I will clean-up it later" and the other famous word of administrators "Ship it now!", So it is too important to avoid all prevents including ignorance, stubbornness, short-time syndrome, arrogance, job security and scheduling. The developer doesn't have to resolute adherence to his own ideas or desires against writing clean code. A developer makes mistake when work reaction instead of action and when feeling own importance and reject cleaning to his code.
The best way to convince yourself to write clean code is to imagine that the other developer guy who comes after you is a homicidal maniac who knows where you live :)
It is too important to get your intent to other developers, the thing you should focus on when coming to give a name for an object or a method. A not very good example of an object name is "theList", where at the time that other developer is going to know that it is a name of a collection, he doesn't know what is the type of items inside and what is the purpose hidden behind this collection. You didn't pass him any description to imagine your imagination, it is all a matter of what is the intent you want to pass for him to work with your code. A good naming example is "ProductList" but the more elegant one is "ProductCatalog".
Naming standards are something like "CamelCase, UpperCamelCase, lowerCamelCase, PascalCase, lower_Case_Underscores or Upper_Case_Underscore". It does not really matter which naming standard you choose, what really matter is to choose any and immediately use it, just have standard and be consistent.
You should also use "nouns" for naming variables, fields, properties and parameters, for example, you can use names like "indexer, currentUser, priceFilter". Moreover, you can use "verbs" to name methods and functions, for example you can use names like "SaveOrder(), GetApplicableDiscount(), FetchCustomerData(), RunPayroll()". But, names like "recdptrl" is not only ambiguous but they are also difficult to be pronounced as well, so it is more better to make it easier when you type it, for example "recordDepartmentRole" or "receivedPatrol".
Comments should not be used to tell what code does, but if you need to explain what code is doing then you need to rewrite the code to make the code more clear, however, we can use comments to describe intent or consequences of code, and we have to avoid lies of comments, where it is truth that we update or move the code, but not the comments, so comments lie and you should avoid that by explaining yourself in code and rewrite the code if it is unclear.
A good comment is the one that describes intent or clarification such as "//Product price of one month ago", gives warnings or consequences such as "//we do ... to make sure that ..." and mentions ToDos such as "//Magic variables should be moved to the configuration file" that is temporary and must be removed when the task is completed.
A bad comment is the one that plays the role of other tools such as "journaling" comment that plays the role of source control, for example "//Wael | 20 Apr 2015 | Fix Bug No####", this is what source control for "i.e. who, what and when", it is recommended to know your tools and make use of them. Avoid "noise" comments such as "//default constructor".Avoid commented code and just delete the code that is no longer in use, it is the role of source control to retrieve old code back if you need so.
It is all about making code better without changing the functionality of the code. Look to your code and ask yourself, is this bad code? you may say no it is good code and no wrong with it, but it may not feel good for others, they may see it a little bit difficult to understand from the first time, they probably need time to scan it to know what it probably needs to do, and that what you would expect from any developer who would work to that code at the first time. So, ask yourself again what you can do to make this code more easir to work with, because after six months from now someone would going to ask you to make a change, and you might not exactly remember how it works, so you have to look to the code to figure it out "at least" to yourself.
Unit Tests is essential, if you do not have unit tests, you don't really know what your code really does, if you do not know what your code does, you can not safely refactor it, refactoring step one is to bring your code under test, refactoring step two is to safely and confidently update the code.
However, you should think in doing something for your code, like hierarchical functions, which is the concept of categorizing functions into "high, mid and low" levels. Go and start with the high level function that is the basic big chunk of what you are trying to implement, then drill down to the more detailed functions that will have the actual functionality. Then, refactor your code by extract methods out of the large blocks, you can use "Ctrl + [R | M]" to do so in visual studio or right click the editor and select "Refactor >> Extract Method". Make sure the new refactored methods is understandable from its name. A fast way to make sure you did the right refactoring is to run the unit tests for each refactoring step. You can even enable run test with each build operation by clicking "Run Test After Build" button on the top of the "Test Explorer". Having unit test in place makes sure we don't accidentally change the functionality of our code. The unit test naming convention you are using will help much finding where exactly the error happen. Roy Osherove, who wrote the "Art Of Unit Testing", recommended a naming scheme "model_operation_result" that slices unit-test-name into three parts, the first is for the model under test, the second part is the under going action and the last part is the result that we are expecting.
The refactor "Dry Principle" says "Don't Repeat Yourself!", it means don't copy, past and modify code but create a common piece of code to be reused in multiple contexts. If you have two sections with the same code they really need to combine, so that we elements the duplication of the code.
Visual studio automatically handles indents when you press "CTRL + [K | D]", that way you can easily see at a which level every statements is placed.
Use "#Region" to organize your code and specify one region for every similar parts of the code, such as fields, properties, constructors, public methods, private methods, events and notify changed members. That way we will have everything very well organized and we can watch them grouped on solution explorer too if we open the "file name >> class name". What we will see in the solution explorer is all the members of the class where everything is grouped together the same way we already did by "#Region".
Using refactoring techniques that way leads us to tell what our code is doing at a high level, and if we need to drill down to details, then we can do easily. Remember, we start with "not a bad code" and here we ended with something that is "very easy to walk up to very approachable", so six months from now if anyone asks you to make updates to that code, you can walk up to it and remember where you are too quickly and navigate to where you need to make these changes.
Books To Read
Clean Code - Book by Robert Cecil Martin
Working Effectively with Legacy Code - Book by Michael C. Feathers
Refactoring: Improving the Design of Existing Code - Book by Martin Fowler
Refactoring to Patterns - Book by Joshua Kerievsky