“Fixit is dead! Long live Fixit 2,” said Meta, releasing the latest version of its open-source auto-fixing linter. This all-new linter was launched with the intention of enhancing developers’ efficiency and competence, encompassing both open-source projects and an extensive array of projects within the internal monorepo. All of this is fine, but how does it fare against Rust-based Ruff released earlier this year?
Amethyst Reese, the project lead and primary engineer for Fixit 2, said on HN, that these are two different linters with very different goals. “While Ruff prioritises speed over all other concerns, and chooses Rust as the method of achieving that goal with a set of generic lint rules, Fixit is focused on making it easy for Python developers to write custom lint rules,” she added.
Further, she said that use of LibCST (which has a parser written in Rust) makes it easy to build lint rules in Python. She said that Fixit permits engineers to craft specific linting rules within their project repository with minimal content, enabling prompt activation without the need for creating personalised plugins or packages, and without the requirement for rebuilding or deploying a fresh iteration of Fixit. “This lowers the barriers to writing rules, and provides a quick development and feedback cycle when testing them,” she added.
Moreover, she also said that hierarchical configuration allows Fixit to fit well into larger monorepos, with each team able to easily control what lint rules run on their code base. “Open source projects homed in those monorepos also have tools to ensure that results are the same when running on internal vs external CI systems,” she added.
Limitations of Fixit
The auto-fixing linter, Fixit, was originally developed for Instagram and later released as an open-source tool, but faced limitations. It lacked the capability to accommodate local lint rules or hierarchical configuration, features that were crucial for the monorepo structure hosting a multitude of projects. Requests from developers to incorporate Fixit into the monorepo were numerous; however, various challenges emerged, resulting in only partial support for a limited set of security lint rules. This diminished the direct benefits that could have been derived for the Python codebase.
Considering the AI/ML shift, Meta embarked on a partial rewrite of Fixit where the crucial emphasis was on embracing an open-source-first approach. The new version, Fixit 2, meets the requirement of both internal monorepos and open-source projects. It also introduced support for local, in-repository lint rules similar to those in Flake8, a more refined command-line interface (CLI), and an improved application programming interface (API) to enable seamless integration with other tools and automation. The risk of generating incorrect syntax is eliminated in Fixit 2 and it suggests and provides automated fixes based on the lint rules themselves, enhancing its utility and accuracy in code improvement.
Creating a fresh lint rule requires just a few lines of code, often fewer than twelve, along with inline definition of test cases. Additionally, you have the flexibility to position the rule adjacent to the code it intends to lint, streamlining the process.
Source: engineering.fb
Moving Away from Flake8
At Meta, Python holds a prominent position as one of the most extensively employed programming languages. The company believes that Python’s attributes of having a user-friendly syntax that is easy to understand, and its extensive collection of open-source libraries that provide pre-built functionality, serve as a pivotal tool.
There are a number of effective linters available in a Python ecosystem. At Meta, Flake 8 has been used since 2016 and has proven highly successful in aiding developers to minimise bugs and maintain a well-structured codebase. Flake8 is a widely used Python tool that combines several static analysis tools to check Python code for adherence to coding style and potential errors. As a linter, it scans your Python code without actually executing it, helping you catch potential issues and maintain a consistent code style.
The flake8-bugbear plugin, an extension for the Flake8 Python tool that provides additional linting rules to catch potential issues and improvements in Python code, was created by Łukasz Langa, while working at Meta. A prominent figure in the Python community, and a developer who has made significant contributions to various Python projects such as Black and worked as Python Software Foundation developer-in-residence and release manager for Python versions 3.8 and 3.9.
Flake8 has been a cornerstone of our code linting approach, but it has limitations. Creating new linting rules requires complete plugins, leading to complex plugins addressing multiple errors. When linting issues arise, Flake8 only provides location details, lacking suggestions for improvements. Consequently, developers experience trial and error to meet linting standards. Furthermore, Flake8 relies on stdlib ast module, hindering the parsing of new syntax features. Thus, adopting new language features hinges on tool updates, potentially slowing down development.
While Meta has explained about Fixit 2, it did not mention anything about where it stands in terms of speed when compared to other linters. Ruff, which is written in Rust, as opposed to others which are Python-based, is the quickest. Ruff outpaces Flake8 by about 150 times in speed on macOS and surpasses Pycodestyle by 75 times, along with outstripping Pyflakes and Pylint by 50 times, among others. Ruff achieves a swift total processing time of around 60 milliseconds for a single file in CPython, making it notably faster.
Source: GitHub