What is Code Quality? β
Lessons learned in over 10 years of helping engineering teams create high quality software.
Hey π this is Luca! Welcome to a β¨Β free editionΒ β¨ of Refactoring.
This is a guest post written withΒ Catarina Gralha, researcher and writer at Codacy, where we explore the practices that help engineering teams create high quality software.
This edition is part of theΒ Refactoring partner program, which gives selected tech leaders the opportunity to write great pieces about engineering culture on Refactoring, while also giving Refactoring subscribers visibility into the products they are building.
I love Codacy personally and I am thrilled to host one of their pieces today.
Code quality is one of the most discussed topics in engineering, yet remains one of the most elusive.
Intuitively, it impacts your teamβs ability to ship fast and often, while having an indirect impact on the customer experience. But how?
At Codacy we work with more than 250,000 developers, so we have a privileged window into their habits. By surveying many of them over the past year, two metrics stand out:
66% of developers are not satisfied with their current code quality.
On average, 45% of development time is spent on maintenance.
How can we do better?
This article covers everything we learned about code quality in over 10 years of working on tools that help exactly with that.
We will talk about:
π What code quality is and why it matters β a car metaphor.
π₯ Habits of top performing teams β to keep code quality high.
π Quality is systemic β creating a system designed to produce quality.
π¨ Using a tool for quality β how tools can support your process.
π Books and resources β by our favorite authors, to deep dive more.
Letβs go!
π What is code quality
We talk of code quality rather than product quality because the former might very well be invisible to the final user.
In fact, we often say under the hood β which is a car metaphor.
In our context, the car is the full product. Like digital products, a car has a UI/UX, which is how the driver (user) interacts with it, and how the car makes them feel.
Then, if we continue with the metaphor, the software should probably be the car engine.
What makes an engine high quality?
Some things are universally important β like reliability and little need for maintenance. Others, instead, largely depend on the car β sports cars might be concerned about performance, while city cars care more about consumption.
This translates well to software.
It is virtually impossible to be good on every measure of quality, so, for many things, it is a matter of what is valuable to you. Brendan Mulholland, co-founder of Recital, wrote a great blog post on this:
When someone talks about quality code, they are implicitly ranking a set of factors [β¦]
Good quality for a startup means prioritizing velocity; for NASA, it means stability and being bug-free. This brings us back to the fuzziness of this level of quality; while many factors of quality can be measured semi-objectively, the rankings of those comes from factors more closely aligned with business-level quality.
The tradeoffs and rankings that are explicitly (and implicitly) incentivized in a given context are the values of that context.
There is also one area where the car metaphor breaks: software evolves.
Evolution gives maintainability a whole other dimension: itβs not just about repairing defects, it is about being able to change the code to support what the future brings.
Research shows that there is a high correlation between businesses that ship fast and often, and those that are ultimately successful. With that respect, high quality code has to be that that is easy to change.
Joel Chippindale, CTO and executive coach, weighed in on this:
This is the definition of high quality code that I have found most useful in my teams because this is what really matters to you as a development team.
Code that is easy to keep changing is code that is enjoyable to work on. It helps us feel smarter and enables us to do more valuable work for the organisations we work for. Everyone wins and thatβs why itβs such a valuable lens to look at code quality through.
What makes code easy to change and enjoyable to work on? Letβs review the most popular traits that we found in top performing teams π
π Readability
As Martin Fowler says:
Any fool can write code that a computer can understand. Good programmers write code that humans can understand.
Code that is readable can be picked up easily by anyone in the team, independently of who wrote it in the first place, and even a long time after it was written.
This makes a lot of difference with maintainability. On healthy teams, readability is the result of many practices, like:
Good separation of responsibilities β to keep code simple.
Meaningful folder structures β to make code discoverable.
Good naming conventions β to make code understandable.
And more. Readability is so strictly related to maintainability, that boundaries are often blurred. As Chris says:
Might go hand-in-hand with readability, but, maintainability also has to do with documentation, readme files, deployment schemes, disaster recovery mechanisms, database modeling, and rollback strategies for new features.
Readability is also helped by including good comments π
βοΈΒ Writing good comments
Commenting code has become a somewhat controversial practice, as many developers believe that good code documents itself.
We found, instead, that top performing teams are consistently good at commenting code, by focusing on the right amount of comments that provide the most value to the codebase.
Here is more about common types of comments:
β Β Comments at the top of the file/class β a few lines to describe the fileβs primary goal can go a long way to help maintainers. It also helps to avoid scope creep over time and to keep the file true to its original goal.
β Comments on complex functions β most functions should be simple enough to be understandable as-it-is, but there are algorithms and processes that cannot be so, no matter how well you write them. In these cases, it helps to include a description of the inputs, the logic, and the outputs of the function.
β In-line comments β while there is value in explaining the workings of a complex function, the need to comment single lines of code is most often a code smell. If people understand what the function does but not some of its lines, there is probably something you can rework, rather than commenting lines themselves.
Code comments are also easier to keep up to date than docs that live elsewhere, as you may update them in place whenever you are making changes to the code itself.
Likewise, it is easy to spot in PRs whenever some code has changed but comments have not, and report it.
π Testing
Good automated testing boosts your confidence in making changes, which in turn makes you go faster. And by catching bugs earlier, it also reduces the time you spend on maintenance.
This all contributes to keeping the quality of your codebase high.
We also find that the importance of good coverage is real. Getting good coverage doesnβt necessarily mean writing an insane amount of tests. Integration tests can easily cover large parts of your codebase by addressing a few, crucial use cases.
One of our customers recently reported:
We hit 70% code coverage across those pieces [of code], and weβve reduced our tech support time by about 60%.
π€ Code reviews + static analysis
It is no secret that code reviews make code quality higher and help share knowledge across the team.
We find that the best teams are good at combining manual reviews with the work of static analysis tools, automatically scanning new code against a set of best practices to suggest areas for improvement.
Good tools can greatly improve your review process. They make the reviewerβs job easier by doing some real heavy lifting, like catching bugs, code smells, vulnerabilities, and taking care of style and formatting conventions.
π¦ Low technical debt
For many teams, technical debt is the #1 offender of their development pace.
We find that great teams are hyper-aware of the state of their debt and are intentional on addressing it. They continuously track debt items, prioritize them against product initiatives, or create dedicated swim lanes for working on them.
They also have strong communication in place with stakeholders. They can advocate for significant refactoring initiatives and tackle them when the ROI is right.
More on managing technical debt π
π Quality is systemic
Most of all, we find that good software quality is the result of good systems and processes rather than individual performance.
A team of average developers working in a system designed to produce quality will eventually produce better results than a group of amazing developers working with processes that are not designed for that.
As Jacob Kaplan-Moss writes, good systems form virtuous cycles:
Great tests catch errors before they become problems.
Tests require a structure that affords the time and space to write them.
That structure exists because engineers are comfortable speaking up when they need extra time to get the tests right.
Engineers are comfortable speaking up because they work in an environment with high psychological safety.
That environment exists in part because failures are seen as systemic, and individuals wonβt be punished, blamed, or shamed.
π Books about code quality
If you want to dive deeper into code quality, here are our favorite books about it π
π Clean Architecture β by Robert Martin.
π Clean Code β by Robert Martin.
π The Pragmatic Programmer β by David Thomas and Andrew Hunt.
π Refactoring β by Martin Fowler.
π¨ Using a tool for code quality
At Codacy, we help thousands of developers ship billions of lines of code daily by automating and standardizing code reviews.
Weβve built a suite of products that help developers act on their software quality, engineering performance, and security:
Quality
Quality is the best-in-class solution for static code analysis. By integrating seamlessly into your workflow, it helps engineering teams save time on code reviews and tackle technical debt.
Users receive notifications on security issues, code coverage, duplication, and complexity in every commit and pull request, along with advanced metrics on the health of the project and team performance.
It supports 40+ coding languages and is available in free open-source and enterprise versions (cloud and self-hosted).
Pulse
Recently, we introduced Pulse to help companies achieve elite engineering performance through data-driven insights.
Pulse lets you assess the health of your engineering culture by measuring DORA metrics and getting guidance on the best DevOps practices you should adopt, based on your current stage.
It is a bridge between product, leadership, and finance, so you can be focused on your business goals.
How did you like this edition? π³οΈ
Amazing β’ Great β’ Good β’ Meh
And thatβs it for this week! If you liked the article, consider doing any of these:
1) βοΈΒ Subscribe to the newsletterΒ β if you arenβt already, consider becoming a paid subscriber. That also gives you access to the community and the curated library.
2) β€οΈ Share it β Refactoring lives thanks to word of mouth. Share the article with your team or with someone to whom it might be useful!
See you next week! π
Luca
Thank you for the amazing article. It is really great!!!
I love this:
As Jacob Kaplan-Moss writes, good systems form virtuous cycles:
1. Great tests catch errors before they become problems.
2. Tests require a structure that affords the time and space to write them.
3. That structure exists because engineers are comfortable speaking up when they need extra time to get the tests right.
4. Engineers are comfortable speaking up because they work in an environment with high psychological safety.
5. That environment exists in part because failures are seen as systemic, and individuals wonβt be punished, blamed, or shamed.