Running Rubocop 3X+ faster with Qlty CLI
Jan 23, 2025
ᐧ
Bryan Helmkamp
ᐧ
5 min read
RuboCop is a fantastic Ruby linter that has been actively developed for more than 12 years. However, it can be slow to run on large projects. By running Rubocop with the Qlty CLI, we can improve its performance by at least 5X and save time every time we lint locally or on CI/CD.
Our engineering team at Qlty has extensive experience writing Ruby. Ruby is known for its lovable code, but it is a highly dynamic language with many ways to use its sizable syntax and standard library—precisely the context where linting is essential for large codebases' consistency, maintainability, and correctness.
In this post, we’ll examine the speed impact of running RuboCop through the Qlty CLI and demonstrate the ability to run RuboCop at least 3X faster.
Baselining RuboCop directly
All testing was performed on an Apple MacBook Air laptop with an Apple M2 silicon running macOS Sequoia 15.
Rails uses Rubocop to lint its codebase of approximately 3,300 code files, so we used that to approximate a large Ruby project that has been active for many years. To establish a baseline for RuboCop speed, we linted the whole Rails repository with these commands:
When run directly without caching, Rubocop lints about 57 files per second on my machine.
Running RuboCop through Qlty CLI
Next, we installed v0.466.0 of the Qlty CLI:
The Qlty CLI ships with over 50 plugins for static analysis tools, including RuboCop. Since we will be testing different performance configurations, instead of using the built-in RuboCop plugin definition, I created a .qlty/qlty.toml
file inside the Rails repository with a basic configuration:
This configuration instructs the Qlty CLI to install the RuboCop based on Rails’ Gemfile and run it using the same we benchmarked above: rubocop --cache=false --format json .
We can now test the speed of running RuboCop via the Qlty CLI:
In this configuration, the Qlty CLI and RuboCop lint about 53 files per second, which is a 6% slowdown relative to running RuboCop directly. This corresponds to the baseline overhead of the Qlty CLI.
Let’s see if we can improve on that.
Adding concurrency for a 3.4X speed increase
First, let’s update the plugin definition to enable batching:
When batching is enabled, rather than running rubocop ., Qlty will identify all the *.rb files in the working directory, split them into groups of 64 targets, and invoke RuboCop multiple times concurrently. Given the number of Ruby files in the Rails repository, this results in 51 batches.
We can test the performance impact of this with this command:
With this simple change, we can now lint 138 files per second, or a speed increase of 2.4X over running RuboCop directly.
Increasing the max_batch configuration option from the default of 64 to 512 increases speed further to 191 files per second, or 3.4X faster than running RuboCop directly.
21X faster caching performance
Both the Qlty CLI and RuboCop support caching linting results to disk. However, since we have disabled both caches in all our testing so far, we thought it would be interesting to test the performance against a primed cache.
The commands used to generate the cached performance data were as follows:
When the Qlty CLI detects a cache hit, it skips running that file through the linter. Therefore, when the cache is fully primed, the Qlty CLI will not execute RuboCop at all.
With a primed cache, RubCop returns cached results at a rate of 470 files per second, and the Qlty CLI returns cached results at a rate of 9,800 files per second (21X faster).
This represents a best-case scenario. In daily use, it’s common for a linter run to use a mix of cached data and newly generated results. Using this lens, running RuboCop through the Qlty CLI will increase performance by 3X to 21X files per second, depending on the cache hit ratio.
Git-aware execution for a 100X speed-up
The final trick that the Qlty CLI uses to speed up linting is Git-aware execution. The Qlty CLI can lint only the files modified in your local Git workspace relative to your upstream branch. This limits the number of files to be processed (either cached or uncached), improving the signal-to-noise ratio of linting results and resulting in a massive speed increase.
Most changes to the Rails codebase affect 0.1%—1% of the code files in the repository (3 to 30 files out of approximately 3,000). Using Git-aware execution reduces the number of target files by 99% with minimal overhead. This increases the theoretical performance by approximately 100X relative to a non-Git-aware approach which lints every file in the repository.
Next steps
The Qlty CLI is free to use and can speed up almost any linter. To run RuboCop and the rest of your code quality tools faster, follow these steps:
Written by
Bryan Helmkamp
CEO, Qlty Software