What is code quality?
Before we talk about code quality tools, let’s see what code quality is. We can agree that code is of high quality if
It does what it is supposed to do
If the code written is not doing what is supposed to do, then it doesn’t meet the very basic requirements, and we can say that the code quality is low.
It does not contain defects or problems
Let’s say that your code does what it is supposed to do, but struggles to perform well on edge cases. Let’s take an example of a mobile app that you start using for communicating with friends and family. You can send & receive messages easily from your friends through this app. One fine day, you want to share a photo with a group and your app crashes. Certainly, there is some issue with the code that has not been tested properly.
It is easy to read, maintain, and extend
Robert C. Martin in his book Clean Code says:
Indeed, the ratio of time spent reading versus writing is well over 10 to 1. We are constantly reading old code as part of the effort to write new code. Because this ratio is so high, we want the reading of the code to be easy, even if it makes the writing harder.
Therefore, code readability is a very important factor in code quality. A code which is readable makes it easier to maintain and extend.
Style guide: following the conventions
Great codebases look like they were written by an individual, when they were worked on by a team.
Consistent code is easier to read & therefore maintain by a team. A style guide serves the purpose of defining a consistent way to write your code. The style guide contains conventions to be followed for writing a code. PEP8 is the official style guide for Python that is recommended to use for writing any python code. Let’s consider a small example. In the python code below, the operators tend to get scattered across different columns on the screen, and each operator is moved away from its operand and onto the previous line. Here, the eye has to do extra work to tell which items are added and which are subtracted:
## No: operators sit far away from their operands income = (gross_wages + taxable_interest + (dividends - qualified_dividends) - ira_deduction - student_loan_interest)
To solve this readability problem, mathematicians and their publishers follow the opposite convention which is more readable
## Yes: easy to match operators with operands income = (gross_wages + taxable_interest + (dividends - qualified_dividends) - ira_deduction - student_loan_interest)
Another official recommendation from Python is PEP-257 Docstring Conventions. A docstring is a string literal that occurs as the first statement in a module, function, class, or method definition.
def complex(real=0.0, imag=0.0): """Form a complex number. Keyword arguments: real -- the real part (default 0.0) imag -- the imaginary part (default 0.0) """ if imag == 0.0 and real == 0.0: return complex_zero ...
There are tools capable of generating documentation directly from the code if the docstrings are consistent in the code.
How to measure code quality?
There are different ways to measure code quality in general. This article talks about some basic tools provided in Python to measure the code quality:
Linters analyze code to detect various categories of issues:
- Logical Issue
- Code errors
- Code with potentially unintended results
- Dangerous code patterns
- Stylistic Issue
- Code not conforming to defined conventions/style guide
Popular python linters
- Pylint: Features like checking coding standards, error detection, refactoring help and more.
- pycodestyle: for checking some of the style conventions in PEP8
- Flake8: a combination of following linters: PyFlakes, pycodestyle, Ned Batchelder’s McCabe script
- Pylama: composed of the following linters and other tools for analyzing code:
Every linter has its advantages and disadvantages and should be picked up based on the project you are working on. If a linter doesn’t give out any warning for any lint, that doesn’t mean that code is always correct, a linter is just a static tool for code analysis. It cannot check if the job that is supposed to be done is done or not.
Formatters automatically format your code based on a style guide. Some popular python formatters are:
as per the black’s documentation, Black is “The uncompromising Python code formatter”. It is my personal favourite because it has minimal configuration and is fast enough. Black is used by some very popular open-source projects, such as pytest, tox, Pyramid, Django Channels, Poetry, and so on. Example usage:
def is_unique( s ): s = list(s ) s.sort() for i in range(len(s) - 1): if s[i] == s[i + 1]: return 0 else: return 1 if __name__ == "__main__": print( is_unique(input()) )
$ black unique.py produces the following
def is_unique(s): s = list(s) s.sort() for i in range(len(s) - 1): if s[i] == s[i + 1]: return 0 else: return 1 if __name__ == "__main__": print(is_unique(input()))
YAPF (Yet Another Python Formatter) is Google’s official python formatter which follows google’s style guide. The documentation is easy to understand the installation and configuration for this formatter.
autopep8 is an unofficial, yet popular, tool that automatically formates Python code to conform to PEP 8. It uses pycodestyle, Python’s official PEP-8 violation checker tool, to determine what parts of the code need to be formatted.
isort is a Python utility/library to sort imports alphabetically, and automatically separated into sections and by type.
from my_lib import Object import os from my_lib import Object3 from my_lib import Object2 import sys from third_party import lib15, lib1, lib2, lib3, lib4, lib5, lib6, lib7, lib8, lib9, lib10, lib11, lib12, lib13, lib14 import sys from __future__ import absolute_import from third_party import lib3 print("Hey") print("yo")
from __future__ import absolute_import import os import sys from third_party import (lib1, lib2, lib3, lib4, lib5, lib6, lib7, lib8, lib9, lib10, lib11, lib12, lib13, lib14, lib15) from my_lib import Object, Object2, Object3 print("Hey") print("yo")
Where to use the code quality tools?
You can use these code quality tools:
- In your IDE/editor, when you are writing your code
- on committing the code
- merging the code or while running the tests
Most modern IDEs and editors have inbuilt support for linters. Some editors like VS Code have great support for linters. You can also run a specific formatter on running the auto-format command in VS Code. Check more about it here.
On Committing the code: Hooks
Like many other Version Control Systems, Git has a way to fire off custom scripts when certain important actions occur through git-hooks. You can use these scripts to run the lints and formatters to block any new code that doesn’t meet quality standards or to automatically format the commit. A useful pre-commit (written in python) is a multi-language package manager for pre-commit hooks. You specify a list of hooks you want and pre-commit manages the installation and execution of any hook written in any language before every commit
When running tests: Continuous Integration
You can also place linters & formatters directly into whatever system you may use for continuous integration. The linters can be set up to fail the build if the code doesn’t meet quality standards. A CI(continuous integration) is a practice of automating the integration of code changes from multiple contributors into a single software project. This means that as soon as a developer merge their code in the main branch, the CI pipeline(for eg: git workflow) can perform some automatic operations like formatting before the code is deployed.
A code is of high quality if it does what it is supposed to do, it does not contain defects or problems and it is easy to read, maintain, and extend. Style guides like A style guide like PEP8 and google’s style guide help serve the purpose of defining a consistent way to write your code. The different tools for measuring and improving the code quality are linters & formatters.
Finally, You can use these code quality tools using:
- IDE/editors - when you are writing your code
- pre-commit hooks - on committing the code using
- continuous integration - while running the tests