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:

git clone https://github.com/rails/rails.git
cd rails
bundle install
time bundle exec rubocop --cache=false

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:

curl https://qlty.sh | sh

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:

[plugins.definitions.rubocop]
runtime = "ruby"
package = "rubocop"
file_types = ["ruby", "gemspec"]
config_files = [".rubocop.yml"]

[plugins.definitions.rubocop.drivers.lint]
script = "rubocop --cache=false --format json ${target}"
target = { type = "literal", path = "." } # Lint entire repo at once
success_codes = [0, 1]
output_format = "rubocop"

[[plugin]]
name = "rubocop"
version = "1.68.0"
package_file = "Gemfile"
package_filters = ["rubocop"

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:

time qlty check --no-cache --no-fix --all

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:

[plugins.definitions.rubocop.drivers.lint]
script = "rubocop --cache=false --format json ${target}"
success_codes = [0, 1]
output_format = "rubocop"
batch = true # ***

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:

time qlty check --no-cache --no-fix --all

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.

[plugins.definitions.rubocop.drivers.lint]
script = "rubocop --cache=false --format json ${target}"
success_codes = [0, 1]
output_format = "rubocop"
batch = true
max_batch = 512 # ***

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:

time bundle exec rubocop --cache=true .

time qlty check --no-fix --all

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:

# Install on macOS or Linux:
curl "https://qlty.sh" | sh

# OR install on Windows:
powershell -c "iwr https://qlty.sh | iex"

# Enable RuboCop in the .qlty/qlty.toml config file
qlty plugins enable rubocop

# Run RuboCop 3x+ faster

Written by

Bryan Helmkamp

CEO, Qlty Software

Code quality and coverage done right

© 2024 Qlty Software Inc.

Code quality and coverage done right

© 2024 Qlty Software Inc.

Code quality and coverage done right

© 2024 Qlty Software Inc.