Code Quality in Python

Code Quality in Python

Hello everyone, I'm Gus, a Python developer at Money Forward Vietnam. I would like to share with you about the code quality in Python. In this post, I will show you how to improve the quality of your own code. Hopefully you can benefit from the practices and tools talked about here.
Code Quality in Python

What is code quality?

Code quality is a measure of how well a piece of code is written. It's not just about how well the code works, but also how well it's written. Code quality is a measure of how easy it is to read, understand, and modify the code.

High-quality code usually has the following characteristics:

  1. It does what it's supposed to do.
  2. It does not contain defects or bugs.
  3. It is easy to read, maintain, and extend.

Let's delve into each of these characteristics to understand the reasons why they matter.

Why is code quality important?

Code quality is important because it makes it easier for others to understand your code, and for you to understand your own code when you come back to it later. It also makes it easier to debug your code, as you can more easily see where the problem is.

Let's see what if code quality does not meet the above criteria.

It does not do what it's supposed to do.

Firstly, meeting requirements is the basis of any software product. If our code does not meet the requirements, then it is not fit for purpose. This is the most important aspect of code quality.

It contains defects or bugs.

If the product we are using has issues or causes problems, we would not tell that it is a good product. The same applies to code. If things break on edge cases and the users get unwanted behavior, they may stop using the product.

It is difficult to read, maintain, and extend.

Most of the time of a software product is spent on maintenance. Imagine that you have to maintain a codebase and the customer requests a new feature. If the code is easy to read and comprehend, you'll be able to implement the new feature quickly.

But if the code is complex, it will take you a lot of time to understand the code or worse, you may make wrong assumptions about the code and introduce bugs. And if the code is not easy to extend, the new feature could break other things. This creates more troubles for you and your team, and no one wants to work with low quality code.

Improving code quality in Python

Python has several tools and practices to ensure high code quality. In this section, we will discuss some of them.

Style guides

A style guide serves the purpose of defining a consistent way to write your code. It helps to facilitate the goal of making code easy to read, extend, avoid common logical mistakes without changing the logical outcome of the code.

There are several style guides available for Python, but the most popular one is the PEP 8 document.

PEP 8 is the official style guide for Python code. It is a set of rules that define how Python code should be written. It is not a strict set of rules, but rather a set of guidelines that should be followed. So we can consider PEP 8 as coding conventions.

PEP 8 covers topics such as:

  • Naming Conventions: How to name your classes, functions, methods, modules, variables, etc.
  • Code Layout: How to format your code properly. This includes indentation, use of tabs or spaces, maximum line length, how to break up long lines, etc.
  • Whitespace: Where to put spaces and where not to.
  • Comments: How and when to use comments.
  • Imports: Recommendations for importing modules and packages.

Another document that is worth mentioning is PEP 257. It is a style guide for Python docstrings. A docstring is a string literal that occurs as the first statement in a module, function, class, or method definition. It's used for documentation of what a function, module, or class does.

Linters

The questions that may come to your mind are: How can we enforce these guides? How can we detect defects and problems in the code? That’s where linters come in.

What is a linter

Visually speaking, lints are small pieces of fluff or dust that somehow get all over your clothes. Clothes look and feel much better without all that lint. Your code is the same. Little mistakes, stylistic inconsistencies, and dangerous logic don’t make your code feel great.

In the context of programming, linter is a tool that analyzes source code to flag programming errors, bugs, stylistic errors, and suspicious constructs. The term originates from a Unix utility that examined C language source code.

When you run a linter, it scans your code and checks for any potential issues or violations of coding standards. It can detect things like syntax errors, unused variables, missing imports, incorrect indentation, and more. 

Python linters

There are many linting tools available for Python. There are also code analysis tools that provide other insights into your code. While maybe not linters by definition, they are usually used side-by-side with linters. Here's the table to summarize all these tools.

Tool CategoryPurposeTools
Error linterDetect syntax errors or other code that will result in unhandled exceptionsPyflakes, Pylint, Flake8
Style linterDetect issues that don't cause bugs but make the code less readable or are not in line with style guides such as Python's PEP 8 documentPylint, Flake8
Code formatterAutomatically reformat your code to follow a style guideBlack, Autopep8, isort
Docstring linter / formatterDetect and format style issues in docstrings that aren't in line with Python's PEP 257 documentpydocstringformatter, docformatter, pydocstyle
Type checkerVerify that your program follows their own type annotations (aka type hints)Mypy, Pyright
Complexity analyzerDetect code that is so complex that they can affect readabilitymccabe, Radon
Security linterDetect security vulnerabilities in your codeBandit, Dodgy

Many linters are actually just wrappers around other linters, for instance:

Flake8 (Error & Style Linter, Complexity Analysis): Flake8 is a bundle of Pyflakes, pycodestyle, and mccabe and merges the output of these programs together.

Pylama: A code audit tool composed of a large number of linters and other tools for analyzing code. It combines the following:

  • pycodestyle (style linter)
  • pydocstyle (docstring linter)
  • Pyflakes (error linter)
  • mccabe (complexity analysis)
  • Pylint (error and style linter)
  • Radon (complexity analysis)
  • eradicate (dead code linter)
  • Mypy (type checker)
  • Vulture (dead code linter)

Integrating into development workflow

It’s useful and better to check code quality frequently. Integrating code quality checks into your development workflow is crucial for maintaining high standards and catching issues early.

As writing code

Most IDEs, including Visual Studio Code and PyCharm, have plugins for linters and can be configured to check your code as you write it. This provides immediate feedback and allows you to fix issues as they arise.

Before committing code

Version control systems like Git allow you to set up a pre-commit hook that runs your linter and tests before allowing a commit. This ensures that the code is always checked before it is committed to the repository.

In CI/CD pipeline

You can set up your Continuous Integration/Continuous Deployment (CI/CD) pipeline to run linters and tests on every commit or pull request. This provides a final check to ensure that all code, even from branches, meets your quality standards before it's merged.

One powerful tool for code quality management is SonarQube. It performs automatic reviews of code to detect bugs, code smells, and security vulnerabilities. SonarQube provides detailed reports on the health of your codebase, enforcing certain quality standards and offering suggestions for improvement. Here's how it contributes to code quality:

  • Code Quality Management: It provides a clear and structured view of coding errors.
  • Security: It detects potential vulnerabilities and bugs.
  • Continuous Inspection: It integrates with your CI/CD pipeline for continuous code quality inspection.
  • Technical Debt Management: It calculates and manages technical debt, i.e., the estimated time to fix all the issues and bugs.

During code review

While tools like linters and static code analyzers are incredibly useful for catching common errors, enforcing style guidelines, and even identifying some security vulnerabilities, they cannot fully replace human code review. Here are some reasons:

  • Understanding Context: Tools can check code against predefined rules, but they can't understand the broader context of the changes. Humans can understand why a change was made and how it fits into the overall project.
  • Catching Logical Errors: Tools are great for catching syntax errors and violations of coding standards, but they often can't catch logical errors that a human reviewer could spot.
  • Code Readability: While some tools can enforce certain readability standards (like PEP 8 in Python), they can't assess whether the code is easy to understand for a human reader.
  • Learning and Mentoring: Code reviews are a great opportunity for team members to learn from each other and for more experienced developers to mentor less experienced ones.

Conclusion

In this post, we went through some of the tools and practices that can help to improve the quality of Python code. We also discussed how to integrate these tools into your development workflow.

Improving code quality is a process. Awareness of the importance of code quality is the first step. Remember, the goal is not to write perfect code from the start, but to continuously improve the code over time.

More like this

Playwright tips and tricks
Feb 02, 2024

Playwright tips and tricks

A Summary of Live Coding a Simple CLI for gRPC Testing at Go Conference 2023
Jul 14, 2023

A Summary of Live Coding a Simple CLI for gRPC Testing at Go Conference 2023