The objective of this blog is
- To briefly discuss - What is Continuous Inspection and What are the deadly sins of a developer?
- To throw some insight into how SonarQube computes Code Complexity. It is pretty different from the traditional metric we all are well aware of – Cyclomatic Complexity.
- To emphasize some of the Programming Principles and Refactoring Idioms, which are directly linked with code complexity.
- To share my favourite mind maps related to Continuous Inspection.
What is it? It is a widespread inspection of software to provide early warning of maintainability and other code quality issues.
"Continuous Inspection is a process designed to make internal code quality an integral part of the software development life cycle. The key concept in Continuous Inspection is finding problems early–when fixing them is still cheap and easy. Under this model, automated code audits are performed daily and made available within an organization." - SonarQube Whitepaper.
There are ten principles of Continuous Inspection defined by SonarQube. Here are my three favourites;
- Software Quality must be part of the development process, meaning that meeting quality standards is one of the demanding requirements to declare development complete.
- Software Quality data must be up to date
- All new issues and existing critical issues must be assigned a clear path and timeline for resolution.
Let's quickly skim through what we call as deadly sins before we see three steps of managing them.
Sonar defines the following seven technical axes, which the Sonar team prefers to call them: "The seven deadly sins of a developer."
- Coding standards: respect coding standards and follow best practices
- Potential bugs: eliminate code violations to prevent vulnerabilities
- Documentation and comments: provide documentation especially for the Public API, the source code
- Duplicated code: isolates and refines duplication, Don't Repeat Yourself
- Complexity: equalizes disproportionately distributed complexity among components; eliminates complexity if possible.
- Test coverage: writes unit tests, especially for complex parts of the software.
- Design and architecture: minimize dependencies
Code-Smells is always my most-liked topic. I did create some mind-map long ago based on few resources on code smells taxonomy; a good shared vocabulary comes very handily while doing code/design review. This picture conveys a lot more than a thousand words – 37264 words to be precise! Check Jeff's blog and Journal of Empirical Software Engineering to get a summary of what each smell means.
It will be a fun exercise to match the code smell name with its corresponding picture representation. Here is the clue: I used the same colour code for each group, narrowing down the selection.
The newer version of SonarQube started showing bad practice as Code Smells (previously called Violations). It is an excellent strategy to call it smell; The term 'code smell' puts psychological pressure on the code developers. No one wants the results of their work to be "smelly". SonarQube can identify a good amount of Code Smells that are easy to find by the machine. However, the remaining tricky code smells need to code/design reviewers' nose to spot them.
Continuous Inspection: Step 1
- Bring and keep the code smells to zero
- Most of the code smell should be identified in the development and the code review phase even before it goes to the mainline (master branch)
A quick note on the code coverage is in order. Code coverage is how much code of the application on the test is being exercised by giving a test read. That number may not mean a lot in itself, but it can be interesting to watch the trend. If coverage decreases regularly, you should look into why that is happening. To keep the coverage constant, the number of test cases should correlate with the Cyclomatic Complexity (we will see this later). As you add more complexity, you should also add more test cases (although in TDD, you should add the test cases first).
Continuous Inspection: Step 2
- Watch out for the code coverage trend: Bench-marking code coverage of the project at the start and end of the sprint is an excellent way to do it.
What could be the Next Steps?
We all know that, Step 1 and Step 2 should be religiously followed. So, let's introduce Step 3 -- Code Complexity.
Code Complexity is intended to identify the software modules that will be difficult to test or maintain. In other words, it provides the quantitative measure of the Clean Code. Two famous complexity metrics are widely accepted. Let's compare and contrast.
- Cyclomatic Complexity
- Cognitive Complexity
McCabe's Cyclomatic Complexity is an old and established measurement of the software complexity. It has been the de-facto standard for measuring the complexity of a method's control flow for a long time.
Cyclomatic Complexity Algo:
The minimum possible Cyclomatic complexity is one because there's always going to be one path. To measure the complexity of a given block of code, start with one and add one every time you find the keywords such as if, else, case, for, for each, do, while, catch.
Though the Cyclomatic Complexity accurately calculates the minimum number of test cases required to cover a method fully, it is not an adequate measure of understandability. Let's see why,
The mathematical model underlying Cyclomatic Complexity gives these two methods equal weight. However, the control flow of the sum of primes is more challenging to understand than that of getWords. The standard Cyclomatic Complexity algorithm penalizes switch/when statements quite heavily. And also, It doesn't include modern language structures like try/catch and lambdas.
Cognitive Complexity breaks from the practice of using mathematical models to assess software maintainability. It starts from the precedents set by Cyclomatic Complexity but uses human judgment to determine how structures should be counted.
Cognitive Complexity Algo:
The score is assessed according to three basic rules:
- Ignore structures that allow multiple statements to be readably shorthanded into one, 2. Increment (add one) for each break in the linear flow of the code, 3. Increment when flow-breaking structures are nested
Additionally, a complexity score is made up of four different types of increments:
Nesting - assessed for nesting control flow structures inside each other, Structural - considered on control flow structures that are subject to a nesting increment, and that increase the nesting count, Fundamental - assessed on statements not subject to a nesting increment, Hybrid - considered on control flow structures that are not subject to a nesting increment, but which do increase the nesting count.
The Cognitive Complexity algorithm gives these two methods markedly different scores that are far more reflective of their relative understandability. More details on how it computes the Cognitive number can be found at SonarQube Documentation.
Continuous Inspection Step 3:
- Use build plugins to check Code complexity whenever we add new class, methods or components and refactor it to see if we can reduce the complexity.
- The number can be tracked at the project level similar to the code coverage to watch the trend. But, the number makes more sense at the individual component level.
However, we can compute like
the project's complexity = project complexity/project lines of code(LOC)
In this way, we should compare the complexity between two major releases by nullifying the increase in LOC.
These are my best-loved books when it comes to Continuous Inspection. The wisdom from these books are like helpline to get clean and stay sober.
- The Pragmatic Programmer
- Refactoring: Improving the Design of Existing Code
- Clean Code: A Handbook of Agile Software Craftsmanship
I just want to end the blog with the summary of code-smells and the summary of refactoring technique and how to put them together to get what we want.