Build1 publisher3 min readPublished
Ruff Does Not Detect Duplicate Code, And That Gap Now Has Its Own Tool
A developer hit the seam between Ruff's speed and Pylint's R0801 and wrote a single-purpose checker called Arid rather than keep waiting. No benchmarks published.
The Engineer · Build desk
Drafted by a language model from the sources cited here and checked against its claim ledger before publication. How we use AISend a correction
What happened
- The author of the post was working on Polaris, described as a fairly large Python project, and running code-quality tools against it.
- The author states that Ruff handles most of what he wants from a Python linter these days, and handles it very quickly.
- Ruff does not detect duplicated blocks of code, and the author says this is not an oversight in his configuration.
- The author states that Ruff simply does not support project-wide duplicate-code detection today.
- Pylint's R0801 checker has been finding similar code for years.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
A developer working on a large Python project called Polaris reports that Ruff, which handles most of his linting and handles it quickly, does not detect duplicated blocks of code, and has released a single-purpose tool called Arid to cover that one job [1][2][3][9]. The consequence is worth stating plainly: according to the post, this is not a configuration you forgot to enable, because Ruff does not support project-wide duplicate-code detection today [4].
What does support it is Pylint. Its R0801 checker has been finding similar code for years, and the same capability ships as a standalone tool called symilar [5][6]. It can ignore comments, docstrings, imports, and function signatures while looking for repeated blocks [7]. It is also, on the author's project, slow enough that he stopped waiting for it [8]. That is the whole shape of the problem: the fast tool most teams now reach for has no duplicate detector, and the tool that has one is the general-purpose static-analysis system people moved away from for speed reasons. A rule category can disappear from a pipeline without anything turning red.
The reason this is not a weekend patch to Ruff is that duplicate detection on Python source is not text comparison. The post walks through two functions, save_customer and save_account, whose executable logic is identical but whose names and comments differ, and argues that if comments and signatures are excluded from the comparison they probably should count as duplicates [10]. Then the confounders stack up: docstrings, imports, blank lines, decorators, multiline function signatures, parenthesized expressions, different physical source ranges, and same-file duplicates that overlap one another [11]. Arid takes Pylint's R0801 as its definition of intent, finding repeated Python source while allowing those elements to be excluded, but explicitly does not reproduce Pylint's implementation [12].
The scope discipline is the interesting part for anyone maintaining a tool. Arid has one production responsibility, finding duplicated Python source code, and the author lists what it therefore does not do: no formatting, import sorting, type checking, complexity analysis, security scanning, or dead-code detection [13]. His stated rule is that if a feature naturally belongs in Ruff, it probably does not belong in Arid [14]. That flows through to structure. One language, so no generic language abstraction; one detector, so no detector hierarchy; no plugin system, no reporter registry, no dependency-injection framework, no async runtime, and no parser abstraction held in reserve for a parser swap that may never happen [15][16]. Parser-specific knowledge stays in the Python frontend, and everything downstream works on Arid-owned data structures rather than parser AST or token types [17]. The author frames this not as an argument against abstraction but as an argument for paying for it when a requirement exists [18].
What to watch. The post contains no runtime figures, no version numbers, and no measured comparison against symilar, so "Pylint was too slow" remains a qualitative claim from one project [19]. Anyone considering the swap should time symilar and Arid on their own tree before believing either. Also watch the author's own word choice: Ruff lacks this capability "today" [4], and a single-detector tool built to mirror R0801's intent is exactly the sort of thing that becomes redundant if the fast linter ever grows a duplicate checker of its own.