High-quality code is efficient and reliable, runs well without bugs, and meets user needs. It can cope with errors or unusual conditions. It is also easy to understand, maintain, and expand with new features. Additionally, its portability means that it can run on as many machines as reasonably possible.

Development teams work with codebases that are constantly changing. They add, delete, and modify existing code to improve speed or implement new features. That means they read a lot of code, so for them, quality equates to readability.

Unfortunately, constant code changes often cause its quality to gradually degrade. This challenge presents an opportunity to examine what makes code readable, understandable, and of a sustainably higher quality.

What is code quality and why should you care?

The easier code is to read, the easier it is to understand and edit, and runs faster and with fewer errors, resulting in its enhanced maintainability and extensibility. All of this leads to faster development and fewer bugs, providing long-term cost reduction.

These characteristics define high-quality code but how can development teams make code easier to read?

You can start by using descriptive variable names, but properly disposing of unmanaged resources also helps. While the former is simple to implement at all skill levels, the latter is easy to overlook — regardless of seniority. Providing descriptive variable names is a basic practice, but one that greatly enhances readability. Effective resource disposal requires knowledge of the tools used and attentiveness to manage them effectively.

In order to best measure code quality, the metrics used fall into one of two groups: quantitative or qualitative.

Quantitative code quality

Quantitative quality measurements can be defined with a numerical value. For example, code coverage — the percentage of code that runs during unit tests — is a discrete value between 0% and 100%. The greater the value, the less chance there is for undetected bugs. Therefore, code quality is likely higher.

Consider the average number of lines of code (LOC) per function or class. Generally, fewer lines result in greater readability. Most seasoned programmers have undoubtedly encountered many 1,000+ LOC functions, and it is unlikely that those were easy to read or understand.

There is also a more complex metric regarding maintainability, called cyclomatic complexity. Although we rarely encounter it, Visual Studio (for example) provides default support for its measurement.

Cyclomatic complexity calculates the number of paths that source code contains. If-then statements, loops, switches, and other control statements all increase your code’s cyclomatic complexity. Unsurprisingly, higher complexity means increased difficulty with maintenance and a greater likelihood that bugs will occur.

While crucial, quantitative metrics provide only part of the picture. Even 100% test coverage does not indicate that the code will be problem-free. Conversely, a high cyclomatic complexity does not necessarily ensure severe maintainability issues.

The interpretation of quantitative code metrics is a subjective practice. One team member may think 50% code coverage is enough, but another member may think 80% is the minimum acceptable. Fortunately, quantitative metrics find balance with their qualitative counterparts.

Qualitative code quality

Qualitative measures cannot be expressed in numbers, but are equally significant in assessing code. Consider the importance of adherence to coding standards, using meaningful names for objects, or establishing a maximum line width across a codebase (the line width is a number, but its effect on code quality is not). Together, these practices can form the foundation of excellent code.

Qualitative measures can be highly subjective. Some programmers prefer longer variable names that express their purpose, while others feel more comfortable using truncated names like ord or cust. Naming conventions vary depending on the language. This makes qualitative metrics more difficult to define, especially for those whose primary languages may differ from those of their peers.

One vital way to enhance code quality and maintain open discussion about subjective quality metrics is to perform regular code reviews. In this process, coworkers can rate the overall quality of one another’s code.

For example, a team of five developers can each commit code that must be reviewed and accepted by at least two peers before it can be merged back into the main branch. In one respect, this process can detect issues that might have gone unnoticed. More importantly, programmers can learn from one another and stay consistently updated on the latest software changes.

Continuous quality: Measuring code quality in a CI/CD pipeline

There are many tools and guidelines to help development teams track and assess code quality. No matter what tools you use, it can be easy to forget to check code before pushing it to source control. That’s why it’s important to integrate these tools in your continuous integration and continuous deployment (CI/CD) pipelines. Doing so ensures that your code quality checks are run on every commit to source control.

For example, a linter can check whether your code adheres to your company’s coding standards and can run on a build or before a commit. Many CI/CD tools offer easy ways to integrate code quality tools. For example, CircleCI has orbs for ESLint, Pylint, and SonarQube. There are many more available.

ESLint can check your JavaScript syntax as well as whether you’ve properly used the var, let, and const keywords. It also assesses whether your variables have an unintended scope, which may lead to unanticipated behavior. Pylint performs the same tasks for Python. You can configure linters to meet your specific needs and preferences.

SonarQube is a powerful tool that can check for thousands of issues in various programming languages. It can detect duplicate code, potential infinite duplication, undiscarded disposable resources, hard-coded credentials, common typing errors, and even SQL UPDATE and DELETE statements without WHERE clauses.

SonarQube has become the de facto standard for scanning code for issues, but there are many capable alternatives, such as Code Climate. An effective code analyzer can inform you about potential problems, best practices, bugs, and even security hazards.

Whether using a linter or code analysis tool, you can make a build fail on certain issues or send out an email or Slack alert that lists all errors and warnings.

It is also vital to set up unit tests and run them in your CI/CD pipeline. Your build will fail if a unit test fails. You can also fail your build if your code coverage falls below a certain threshold.

Conclusion

Code quality can improve the readability, maintainability, and extensibility of your code. Better quality means less technical debt and fewer bugs, leading to easier development and lower costs.

Building code quality into your development process does not have to be difficult. Ideally, you have done so from the beginning, but it’s never too late to start measuring and improving your code quality.

To learn more about metrics you can use to evaluate and improve your team’s velocity and performance, read How to find and monitor engineering metrics for team success.