Continuous Inspection: How to Define, Measure and Continuously Improve Code Quality
One basic but important customer expectation is that their software product should be of very good quality. While this expectation makes sense, what exactly does “good quality” mean? Here are characteristics of good quality software:
Software should be able to perform all of its functionalities as expected (i.e., defined in acceptance criteria).
Software should meet all non-functional “ilities” (e.g., scalability, reliability, etc.).
Code quality should be great with minimal technical debt.
When it comes to code quality, people in the software industry have different perceptions and interpretations of it. Most of the time, these interpretations are abstract and do not necessarily define code quality in measurable terms. So before we move any further, let’s define what code quality exactly means.
“Code quality is an indicator of how quickly a developer can add business value to a software system.”
The Seven Deadly Sins of Code Quality
Now that we know what “code quality” means, the next relevant questions are, “How do we measure it, and how do we get the sense of it in real time?” Code quality is measured (or rather, the lack of it is measured) in form of the “seven sins of code quality.”
Bugs and Potential Bugs. Bugs and potential bugs make up the most urgent sin, as it shows what’s wrong in your code currently and what can go wrong tomorrow (e.g., NullPointerException).
Coding Standards. Consistent and agreed upon coding standard and style is much easier to follow rather than multiple coding styles in the same project. A more serious example of this type of sin is the failure to follow naming conventions.
Duplications. Although duplications don’t seem like a big deal, they are not efficient in the long run. Similar to Murphy’s Law, the more places into which a chunk of logic has been duplicated, the more likely it will need to be changed – and probably with a high level of urgency or criticality.
Lack of Unit Tests. Unit tests help keep bugs and regressions from slipping into production code. And when you make a change to existing code, they help you know that you didn’t break it.
Bad Distribution of Complexity. It’s okay for a program to have some complex files and methods, but if you have too many of them, the next developer who has to work on the application will have a hard time understanding what’s going on in the code. And if he/she has a hard time understanding it, he/she will have an even harder time successfully modifying it.
Not Enough or Too Many Comments. This is a measure of maintainability. Ideally, the code should be clear enough that one shouldn’t require to look at comments. However, comments become more important when the libraries and APIs are exposed to clients. Instead of looking into the code, one should be able to find the relevant information to use the APIs using coding documentation.
Spaghetti Design. Spaghetti design is having a high level of complexity at the project architecture level rather than in a single method or file. New developers on the team will have a hard time understanding how the project is organized and where new code should be put.
So now as we know the different metrics for measuring code quality, let’s see how to measure them.
How to Measure Code Quality
Code quality needs to be measured in the form of density; abstract numbers are useless. For instance, 400 potential bugs as a number in itself doesn’t tell the severity of the issue. However, when one talks about it in form of potential bug density, you get a better picture. Also, it’s important to observe the trend of code quality throughout a specific period of time.
There are tools that measure code quality and provide a real-time snapshot of code quality through reports. Although these tools are great resources, reports are rather reactive and not actionable. It makes much more sense for a developer to discover the error as soon as she/he makes one. Without fixing the error, such a system wouldn’t allow her/him to move further. That sounds much more proactive, doesn’t it?
In next section, let’s see how to apply code quality checks in a proactive way.
Proactive Code Quality
Step 1: Use IDEs with the necessary code quality plugins (i.e., for Java FindBugs, Checkstyle, PMD, PMD-CPD) and install them so that you catch the problem as you type the code in IDE. If you want to take this approach a step farther, you can use Puppet and Chef to install a consistent developer environment on each machine so that there is no question about plugins installed or not installed, versions of different application software and tools, etc.
- Step 2: In your automated build scripts, install code quality plugins and run them as part of the automated build. Apart from creating reports, these plugins should break the build if the quality metrics dip below the defined threshold. For instance, if the test coverage dips below 75%, the build would fail. Similarly, if the FindBugs plugin catches some severe issue, the build would again fail.
- Step 3: Run these code quality plugins (e.g., FindBugs, Jacoco, etc.) with each code commit in a Continuous Integration (CI) environment. If the threshold gets broken, the build would fail, and the appropriate notification (e.g., email) would go the relevant stakeholders. The team would stop the line and fix the build immediately. For instance, it’s fairly easy to install CI plugins on Jenkins for FindBugs, Checkstyle, PMD, PMD-CPD, and Jacoco so that you can see the trend on quality at any point of time. To illustrate these approaches, below are some snapshots of quality results with different quality plugins on Jenkins.
Code quality measurement and continuous improvement is not about reactively generating reports and making plans to improve it. Instead of fixing quality issues when they already raise their heads in reports, you should proactively not let them happen in the first place. Apart from quality plugins used with automated builds, IDE plugins and CI plugins help a lot in achieving the holistic agenda of clean code.