Does code quality matter? Using SonarQube with legacy code bases
"Code quality" is a slippery concept that is defined by a combination of different factors. Good quality code should to be readable with a clear and consistent structure. It needs to perform well, scale effectively and demonstrate some resilience. It should be secure. It should be testable. Above all, code needs to be adaptable and extensible so it can absorb long term change.
How do you assert code quality on legacy code bases? Static tools such as SonarQube are used to provide quality gates for development as they promise an objective measure of code quality. The dashboards are particularly beloved by management teams as they offer the means of assessing and comparing applications and teams.
This can encourage an unhealthy gamification of code quality. These metrics are not necessarily a clear and objective measure of the condition of a code base. They tell us even less about the performance of a team.
Are we measuring the right thing?
Attempts at SonarQube adoption often fail to gain traction with development teams. The concern is that the issues flagged by SonarQube are not relevant to the problems they are facing on a day-to-day basis.
Older code bases are beset by problems that cannot be identified by a set of generic pattern-matching rules. Teams will be grappling with obsolete technology, scalability challenges, unstable implementations and unwise architectural decisions. None of this will be picked up in code quality metrics.
SonarQube talks in terms of "technical debt", but this has very little to do with the phrase originally coined by Ward Cunningham. He was not suggesting that "debt" was caused by badly-written code. The debt metaphor was originally coined to describe how a code base is undermined over time by evolving requirements. It’s these unintentional design mistakes based on a partial understanding of a problem that make legacy systems hard to work with.
Developers tend to prefer code that is consistent and readable, but that’s only one aspect of code quality. You also need code that is performant, secure and easy to change. These things are all very hard to measure in any meaningful, objective sense.
In the absence of genuine, objective measures static analysis tends to dwell a little too much on style rules. You can’t check that application-specific patterns have been asserted, measure the separation of concerns or determine whether nuances around performance best practice are being followed.
Applying SonarQube to existing code bases
Any code base that has not been written alongside SonarQube is likely to register thousands of rule violations and man years’ worth of technical debt. This does not necessarily mean that the application is unfit for purpose.
This is recognised by SonarQube who actively discourage developers from getting hung up on the numbers. The idea is to support a more gradual approach to refactoring rather than removing all the violations from each file that is updated. Developers should always seek to leave the camp ground a little tidier than when they found it.
SonarQube use the metaphor of a "water leak" for dealing with legacy code bases. Best practice discourages an immediate focus on existing issues – i.e. the "puddle of water on the kitchen floor". Instead, your priority should be to sort out the "source of the leak" and stop any new issues getting in.
This allows you to declare a kind of amnesty on quality issues where you focus on the quality of any new code. Custom quality gates can be defined for each individual code base to define rules based on complexity, duplication, code smells, bugs and vulnerabilities. You can also define your own rule set for each code base if you’re up to handling the administrative overhead.
The problem with this approach is that it can be difficult to disentangle the new from the old. Specifying test coverage for new code may be unrealistic when the entire structure of the application dependencies inhibit unit tests.
Does code quality really matter?
Beware of cargo cult quality. Rigorously following a set of rules will not necessarily give rise to good code quality. After all, J.M. Juran observed that the most important definitions of “quality” are that a product should meet the needs of customers and be free from defects. This is not the same thing as passing a set of generic rules.
Code quality rules tend to matter more to developers who prefer to work with consistent style (normally their own!). This is less of a problem than it used to be as modern languages and tools as they tend to force a certain level of consistency. Static analysis style rules hark back to an era where languages such as COBOL allowed for a much wider range of coding styles.
There can also be a trade-off between code quality and rate of delivery. No matter what the clean code brigade might say, writing “good quality” code takes time. All those unit tests and carefully sculpted methods don’t happen overnight. Investing on the quality of code is often regarded as a privilege which is only granted to products that have already proven successful.
That said, it's always worth running a code base through it to check for the higher priority issues. It can also be a learning experience for developers to be exposed to the rule set. SonarQube can be a useful means of catching stray issues and verifying some aspects of code design. However, it’s not necessarily a guarantee of "code quality".