Now-a-days, performance and security are very crucial aspects of business prospective. If your product is not good in performance and not secure as well, then it is for sure that you are going to lose your reputation. Performance and security gives the positive impact towards,
- Customer trust
- High volume of business
- Reputation etc.
Because, these two aspects are the keys to any software product. We can achieve these both in many ways. In this article, I am going to discuss the best practices of coding standards.
Let's say, we have a business requirement where every developer can write code in their own way to meet the business requirement. But, what makes the difference between you and them. As per my understanding, in the most cases, the developer thinks only from one angle which is the functional requirement to meet the business requirement, but remember, that's not enough. The real developer should think of multiple aspects before jumping into the implementation including both, functional and non-functional aspects. Here, I mentioned one aspect, the non-functional requirement. This aspect plays a vital role along with the functional requirement. Non-functional requirement includes the following:
- User friendliness etc
As a developer, we have to consider the above points along with functional requirement. Going forward, I will show you few of the best coding practices.
Coding standards help writing the easily understandable code and maintaining the consistency. I highly recommend to every software programmer or developer to follow the coding guidelines to help improving the readability of your source code and making software maintenance easier. I have listed few best coding practices which provide:
- Great performance
- Minimized complexity
- Easier maintainability etc.
Given below are few of the best coding standard practices:
- Assignments should not be made from within sub-expressions
- Files should not have too many lines of code (maximum 1000 lines in a namespace)
- Methods should not be too complex (Default value is 10).
- Lines should not be too long (maximum length is 200)
- Methods should not have too many parameters (Default value is 7 )
- switch statements should not have too many case clauses (Default value is 30)
- Control flow statements do, while, switch, if, foreach, for and try should not be nested too deeply (Default value is 3)
- Expressions should not be too complex
- Generic exceptions should not be ignored
- Types should be named in Pascal case
- switch case clauses should not have too many lines
- Classes should not have only private constructors
- async and await should not be used as identifiers
- Boolean checks should not be inverted
- Use break statements only with switch cases, don't use except switch cases.
- TODO tags should be handled and add summary to every method in the file and Methods should not be empty
- Enumerations Flags with zero-value members should be named None
- Fields that are only assigned in the constructor must be readonly.
- For Parameters with [DefaultParameterValue] attributes should also be marked with [Optional]
- Empty default clauses in a switch should be removed
Assignments should not be made from within sub-expressionsAssignments within sub-expressions are hard to identify and make the code less readable. From the below table, we can observe that the preferable column contains == operator. It is also a common mistake to write "=" when "==" was meant to be written. Ideally, the expressions should not have side-effects.
Reason: Assignments within sub-expressions are hard to identify and make the code less readable.
Files should not have too many lines
ProblemA source file containing too many lines of code with too many responsibilities causes difficulty to understand it and makes it harder to maintain.
SolutionIt's advised to break down the responsibilities into smaller piece as methods which focus on well-defined tasks. Then, merge them at last in calling the method. Those smaller files will not only be easier to understand but easier to test also.
Default value: 1000
Methods should not be too complexProblem
Cyclomatic complexity of a function should not exceed a defined threshold. If that exceeds the limit, it causes:
• Poor performance
• Takes lot of time to execute the code which results performance impact
• Difficult to maintain the code
• Difficult to understand
We can use Task Parallel Library (TPL) to overcome such complex code execution.
Default value: 10
Lines should not be too longProblem
Having too long line of code that it scrolls horizontally, that makes it difficult to understand the complete code.
It's advised to split the lengthy line of code into some pieces which makes you easy to understand.
Maximum length: 200
Methods should not have too many parameters
A long parameters list creates a new structure to contain so many parameters.
Encapsulate multiple parameters into a single object.
Ex: Instead of String fname, String lname, String address, String contact-number etc. into a object type, let's define them in a Person object containing all these fields and pass this object type as a method's input parameter.
Default value: 7
SWITCH statements should not have too many CASE clausesProblem
Usually, it is an attempt to map two sets of data. The real map structure will be more readable and maintainable and it should be used instead.
CASE clause content should be minimized by dividing into methods
Mandatory: default case
- Control flow statements IF, FOR, FOREACH, DO, WHILE SWITCH and TRY should not be nested too deeply
Nested flow statements or multiple conditions cause the complex code which is hard to understand. Also, multiple nested flow statements or multiple conditional statements cause the performance issues.
Replace control flow statement conditions into multiple methods (splitting condition based code into multiple methods), and call them in the targeted area.
Expressions should not be too complexProblem
The complexity of an expression is decided by the number of ||, && and condition? if True: if false operators it contains.
Breakdown multiple conditions into methods and call them in the targeted area. Sample code is, as shown below:
Generic exceptions should not be ignored
If exception occurs in your code, then it's bad practice to simply ignore it. Instead, it is good to handle it properly and if you need, you log them.
Types should be named in Pascal caseAs per coding standards types (structure, class), name should be in Pascal case.
SWITCH case clauses should not have too many linesProblem
CASE clause contains too many statements causes difficult in readability.
The content of CASE clause should be moved in a dedicated method, as shown below:
Default threshold: 5
Classes should not have only PRIVATE constructorsProblem
A class having only a private constructor cannot be instantiated and it seems to be worthless code.
Class should contain public constructors as well. Sample code is, as the following:
ASYNC and AWAIT should not be used as identifiers
async and await are reserved keywords. So, we should not use them for variable names in order to avoid the confusion. It is advisable not to use these keywords.
Boolean checks should not be invertedProblem
Using inverted Boolean checks is costly and complex.
Instead, we should use the opposite Boolean comparison. Sample code is written below:
- Break statements should not be used except for switch cases
Break statement makes it difficult to read the code. Most preferable, every loop should have a single termination point. And, it's advised to use close braces for every condition and also, use else part for every if condition.
- TODO tags should be handled and add summary to every method in the file and methods should not be empty
If any method is empty or something you want to implement at later point of time, then in such cases, use TODO tags within the method; which states that something needs to be implemented in TODO tags area. Outside of the methods, write some summary by stating the actual use of the method. So that, at the later point when you forgot, if you filter with TODO keyword, it will give you the filtered result and there it will remind you to write something. And by seeing the summary part, everyone can easily understand the purpose of the method.
- Flags enumerations zero-value members should be named NONE
We should use None flag for 0 value while defining ENUM and we should make a standard practice to maintain the consistency. Sample code is, as below:
Fields that are only assigned in the constructor should be read-only
By the name itself, we can say that only read-only members should be assigned in a class constructor. If member variables are not declared as read-only whereas we are assigning their values in a class constructor, then unnecessarily we are creating confusion. So, to avoid such confusion, if you want to assign field's value in a class constructor, then mark those fields as read-only, otherwise don't use fields inside class constructor which are not marked as read-only.
- Parameters with [DefaultParameterValue] attributes should also be marked with [Optional]
If the caller provides the value for a parameter at any cost, then it's useless to set the default value for a parameter. The DefaultParameterValue must be used in combination with Optional because DefaultParameterValue is non-usable without optional.
Empty DEFAULT clauses in a SWITCH should be removed
If you use default clause in switch case, then it should contain an appropriate action. If not, then there is no meaning of using default clause.
Fiends, if you use the above coding standards, then really you will get lot of benefits, as listed below:
- Application performance will be improved.
- Code maintainability will be easier.
- Code re-usability can be acheived.
- Easy to identify bugs.
- Code complexity can be reduced, and many more...