We are publishing this checklist for the software developers and engineering managers to perform effective code reviews to deliver the best quality software. This checklist also helps the code reviewers and software developers to gain expertise in the code review process. Use this checklist as a reference to guide you through your code reviews.
What is a good code?
Good code complies with the following principles:
- Is the code easily understandable?
- Is the code configurable?
- Is the code revertible?
- Does the code follow coding standards/guidelines?
- Is the code redundant?
- Are there proper unit test cases for the written code?
- Is there proper logging to enable you to debug any reported issue?
- Does this function or class have multiple responsibilities?
Detailed code review checklist:
The following code review checklist gives an idea about the various aspects you need to consider while reviewing the code:
Code Formatting
While going through the code, check the code formatting to improve readability and ensure that there are no blockers:
Indentation and Whitespaces:
Use alignments (left margin) and proper white space. Also, ensure that the code block starting point and ending point are easily identifiable.
Naming Convention:
Ensure that proper naming conventions (Pascal, CamelCase etc.) have been followed.
Visibility:
The code should fit on the standard 14-inch laptop screen. There shouldn’t be a need to scroll horizontally to view the code. In a 21-inch monitor, other windows (toolbox, properties etc.) can be opened while modifying code, so always write code while keeping in view a 14-inch monitor.
Commented code:
Remove the commented code as this is always a blocker while going through the code. Commented code can be obtained from Source Control (like SVN) if required.
Code Architecture
Separation of Concerns
Most modern applications follow the MVC framework. It is very essential to split the code into multiple layers and tiers as per requirements (Data, Presentation and Business layers)
Design Patterns
Use appropriate design patterns (if it helps), after completely understanding the problem and context.
Scalability
Every developer must spend time understanding the underlying infrastructure on which the code is deployed. With the advent of cloud computing, the applications are behind load balancers and are horizontally scaled according to the load.
Identify Bottlenecks:
Identify the components of the infrastructure which cannot be scaled. Ensure appropriate strategies like sharding/caching are applied.
Coordination:
Ensure that the different instances can coordinate on tasks. Verify that background and async tasks are not picked up by more than one instance of code.
Coding best practices
- No hard coding, use constants/configuration values.
- Group similar values under an enumeration (enum).
- Comments – Do not write comments about what you are doing, instead write comments on why you are doing it. Specify any hacks, workaround and temporary fixes. Additionally, mention pending tasks in your to-do comments, which can be tracked easily.
- Avoid multiple if/else blocks.
- Use framework features, wherever possible instead of writing custom code.
Non Functional requirements
Maintainability (Supportability)
The application should require the least amount of effort to support in near future. It should be easy to identify and fix a defect.
Readability:
The code should be self-explanatory. Get a feel of story reading, while going through the code. Use appropriate names for variables, functions and classes. If you are taking more time to understand the code, either code needs refactoring or comments have to be written to make it clear.
Testability:
The code should be easy to test. Refactor into a separate function (if required). Use interfaces while talking to other layers, as interfaces can be mocked easily. Try to avoid static functions, and singleton classes as these are not easily testable by mocks.
Debuggability:
Provide support to log the flow of control, parameter data and exception details to find the root cause easily.
Configurability:
Keep the configurable values in place (XML file, database table) so that no code changes are required if the data is changed frequently.
Reusability
- DRY (Do not Repeat Yourself) principle: The same code should not be repeated more than twice.
- Consider reusable services, functions and components.
- Consider generic functions and classes.
Reliability
Ensure proper exception handling and memory cleanup is added to the written code.
Extensibility
Easy to add enhancements with minimal changes to the existing code. One component should be easily replaceable by a better component.
Security
Authentication, authorization, input data validation against security threats such as SQL injections and Cross Site Scripting (XSS), encrypting the sensitive data (passwords, credit card information etc.)
Performance
- Use a data type that best suits the needs such as StringBuilder, or generic collection classes.
- Lazy loading, asynchronous and parallel processing.
- Caching and session/application data.
Usability
Put yourself in the shoes of an end-user and ascertain if the user interface/API is easy to understand and use. If you are not convinced with the user interface design, then start discussing your ideas with the business analyst.
Object-Oriented Analysis and Design (OOAD)
Single Responsibility Principle (SRS):
Do not place more than one responsibility into a single class or function, refactor into separate classes and functions.
Open Closed Principle:
While adding new functionality, existing code should not be modified. New functionality should be written in new classes and functions.
Liskov substitutability principle:
The child class should not change the behaviour (meaning) of the parent class. The child class can be used as a substitute for a base class.
Interface segregation:
Do not create lengthy interfaces, instead split them into smaller interfaces based on the functionality. The interface should not contain any dependencies (parameters), which are not required for the expected functionality.
Dependency Inversion:
Do not hardcode the dependencies, instead inject them.
In most cases, the principles are interrelated, following one principle automatically satisfies other principles. For e.g: if the Single Responsibility Principle is followed, then Reusability and Testability will automatically increase.
In a few cases, one requirement may contradict another requirement. So trade-off based on the importance of the weightage, e.g. Performance vs Security is necessary. Too many checks and logging at multiple layers (UI, Middle tier, Database) would decrease the performance of an application. But few applications, especially relating to finance and banking require multiple checks, audit logging etc. So it is ok to compromise a little on performance to provide enhanced security.
Tools and Tips
- The first step while assessing the code quality of the entire project is through a static code analysis tool. Use tools (based on technology) such as SonarQube. There is a myth that static code analysis tools are only for managers.
- To track the code review comments use Source control like Gitlab or Github or the tools like Crucible and Bitbucket.
Conclusion
The above code review checklist is not exhaustive but provides a direction to the code reviewer to conduct effective code reviews and deliver good quality code. Initially, it would take some time to review the code from various aspects. After a bit of practice, code reviewers can perform effective code reviews, without much effort and time. If you would like to become an expert code reviewer, this code review checklist serves as a great starting point. Happy Code Reviewing!