Introduction to Linters: Code Quality Gatekeepers
In the vast world of programming, the production of high-quality code is paramount. Well-structured, error-free code that complies with best practice standards is essential to guarantee the maintainability, reliability and efficiency of our applications. Particularly when using Python, which is a very permissive language where it's easy to write nonsense without realizing it. That's where linters come in.
Linters are static code analysis tools that act as guardians of code quality. They scrutinize your code for errors, bad practices, and violations of coding conventions. By identifying these potential issues during the development stage, linters enable you to rectify them before they turn into hard-to-debug bugs and to follow the right code philosophy : Zen of Python. Some popular linters include Pylint, Ruff, Flake8, ESLint and RuboCop. Each is specific to a programming language and offers a range of rules and features to analyze your code.
Meet Ruff: Your New Linter for Fast Clean Code
Tired of waiting your linter to run? Use Ruff. In just a few months of existence, Ruff has surpassed all its competitors. Indeed, Ruff stands out with its features and fast execution, making it a wise choice for your development projects.
Why choose Ruff over other linters?
Firstly, unlike Pylint and Flake8, Ruff offers an AutoFix feature, allowing automatic correction of many errors in your code. This saves you valuable time by avoiding tedious manual correction tasks. Without any effort, Ruff can bring significant improvements to your code, while adhering to defined rules and best practices.
Another advantage of Ruff is its fast execution. Thanks to optimized algorithms written in Rust, Ruff efficiently analyzes your code and provides results almost instantly. Indeed, most linters currently in use are written in Python, which is much slower than Rust.
Ruff is designed to be used daily in your projects. So, the seconds gained on its execution multiply over the course of the day make a real difference to efficiency and comfort in the long run. It easily integrates into your development workflow, allowing you to run regular analyses to maintain code quality at a high level. Ruff accompanies you at every step of your development process making it easier for your colleagues to review your code.
Pylint vs Ruff
Pylint and Ruff are often compared on the rules they cover respectively. While both linters cover more than 400 rules, they have less than a hundred in common. A closer look reveals that the difference lies mainly in typing rules. Indeed Ruff, doesn’t really handle typing. It is actually not a big deal since you should use Mypy on your project anyway. It is worth mentioning Black as well, which is also an excellent complement to Pylint and Ruff.
As a consequence, we recommend that you use Ruff, Black and Mypy together to have the best combo currently existing for static analysis.
Ruff, a Linter to Rule Them All
Ruff offers an easy management of predefined rules
A key point when talking about a linter is the rules it covers. In terms of rules, Ruff covers more than 500 lint rules for now and this value continues to rise. It allows to configure rules according to your needs and coding style. For example, you can enable docstring rules to ensure all functions and classes are properly documented.
The rules are divided into numerous categories symbolized by letters (E, F, D, PL etc.). Each category contains several rules, identified by a number. This means that a rule is linked to a code, as in the following example: 'D401' (in this case, category D corresponds to rules linked to docstrings). This is particularly useful when configuring Ruff and, especially, the rules you want to apply. These codes can be used to refer to subsets of rules. For example, "D4" refers to all rules whose code begins with "D4".
You can find the exhaustive list of rules covered by Ruff here. Rules can be configured using a pyproject.toml or ruff.toml file in your project.
The rules in the select list are the rules we want on the project and the ones in the ignore list are the ones we want to ignore. This way you can choose precisely the rules you want to include and keep in your project.
Now that we know how to configure the rules we want, let’s dive deeper into the rules covered by Ruff, unveiling its extensive capabilities for ensuring code excellence.
By default, Ruff covers Rules of type E and F. Those are basic rules from Pyflakes and pycodestyle. As a matter of fact, Ruff alongside with Black can be used as a drop-in replacement for Flake8 if you are working with Python 3. Those sets of rules are the Ruff starter pack, but you should definitely add more, if not all.
Indeed, among the fundamental rules provided by Ruff, you will find guidelines for proper indentation, consistent naming conventions (N set of rules), and documentation aspect of your code (D set of rules). These rules act as a foundation for maintaining code readability and fostering collaboration within your development team.
Additionally, Ruff incorporates notable rules that address specific code patterns and potential issues. One such rule is mccabe, which detects sections of code that are overly complex and may benefit from refactoring. Here is an example of refactoring made by mccabe rule. The original code had a heavy structure with multiple “if” statements that could be simplified into a clearer version.
Another noteworthy rule is isort, which ensures a consistent and organized order of imports, reducing clutter and improving code structure. If you are already using Isort as a package, then you don’t need it anymore, since it’s part of Ruff rules.
Ruff also offers rules for commonly used Python libraries such as Pandas. Unlike Flake 8, Ruff doesn't need any plugins to process rules on Pandas. It also differs from Pylint, which simply doesn't implement Pandas-specific rules. So, stop naming your DataFrame ‘df’ and be kinder to your future self: use Ruff.
Don’t forget that if a rule seems too annoying to you or with too little added-value, you can easily parametrize Ruff to ignore this rule. You can even ignore the rule on a single line using the ‘noqa’ syntax if it's more convenient for you. Example:
import os # noqa: E401
How to Setup Ruff Like a Pro
To be able to use Ruff in your own projects, you can easily install it with PiPy or other packages managers such as Homebrew or Conda:
pip install ruff
Then you can launch it with this command:
ruff check path/to/code/to/file.py
If you consider using Ruff on your project, you should definitely take a look at the official documentation to get the most of it.
Make Ruff part of your development workflow
Launching Ruff by hand is a start, but it's much better when it's automatic and a natural part of your workflow. Here are a few ways of using Ruff in an optimized way:
- Integrate Ruff in pre-commit hooks. This way, Ruff will automatically run on your codebase before each commit, preventing the introduction of code that violates the defined rules. To use pre-commit hooks, please follow pre-commit official documentation.
- Incorporate Ruff into your Continuous Integration (CI) pipeline (Github Actions for instance). By configuring Ruff to run as part of your CI process, you can automatically check the code quality of every code change before any merge request.
- Take advantage of Ruff's VsCode extension and Ruff - IntelliJ IDEs Plugin. This extension provides a seamless integration with the IDE, offering real-time feedback and suggestions as you write code.
- Generate your projects from scratch using Sicarator: the project generator from Sicara recently open-sourced. This tool generates an optimized project template including Precommit hooks and a CI config that both uses Ruff.
By leveraging these integration techniques and tools, you can make the most of Ruff and promote clean code in your projects.
Are you looking for clean code experts ? At Sicara, we combine Ruff with other static checking tools such as Black and Mypy (python type checking tool) for even more code fiability, don’t hesitate to contact us!