Skip to main content
Guides

Ruff Format vs Black: Is It Really a Drop-in Replacement?

Pyobfuscate Team··8 min read
Quick Answer

`ruff format` is a near-drop-in replacement for Black — and far faster. I formatted 124 real files (Flask + Rich source) with both: 107 of 124 (86%) came out byte-identical, and ruff format finished in 0.17 s versus Black's 4.86 s (~29× faster). The 14% that differed were minor, valid style choices (how a long assert message wraps, f-string quote normalization) — not bugs. If you're on Black today, switching touches a handful of files cosmetically. You can format Python with Ruff right now, no install, in our Ruff formatter online.

"Ruff can format too" is easy to say; the question that actually matters if you already use Black is: will `ruff format` produce the same output, or will switching create a giant noisy diff? So I tested it properly — I took 124 real Python files (the source of Flask and Rich), formatted one copy with Black 26.5.1 and another with ruff format (Ruff 0.16.1), and compared them file by file. Real numbers below.

Short version: they agree on the large majority of code, ruff format is dramatically faster, and where they differ it's cosmetic. Below I show exactly how identical they are, the exact nature of the disagreements, how to configure Ruff's formatter, and how to run it online. If you want to try it on your own code first, our Ruff formatter online runs the official Ruff WebAssembly build in your browser — nothing uploaded.

What ruff format is

ruff format is Ruff's code formatter — the counterpart to ruff check (the linter). It's deliberately modeled on Black: the same opinionated, low-config philosophy, the same 88-column default line length, the same general style. The difference is that it's written in Rust and built into the same binary that lints, so one tool covers formatting *and* linting *and* import sorting instead of three.

The design goal Astral set was explicit: ruff format should be Black-compatible, so teams can adopt it without re-formatting the world. The question is how well that goal holds up on real code — so I measured it.

The compatibility test: 124 real files

I formatted the same 124 files (Flask + Rich source) two ways — once with Black, once with ruff format — then diffed the two output trees. Here's what came back:

ResultFilesShare
Byte-identical to Black107 / 12486%
Differed (minor style)17 / 12414%
formatting 124 files with each tool...
black        : 4.86 s
ruff format  : 0.17 s

17 files differ / 124 total
identical: 107
Real output — format 124 files with each, then diff

Two takeaways. First, 86% of files were exactly the same — for most code, Black and ruff format are indistinguishable. Second, ruff format did the whole job in 0.17 s versus Black's 4.86 s — about 29× faster. That speed gap is the same story as the rest of Ruff: native Rust versus a Python program, and it compounds across a big repo and every save in your editor.

86% identical is *higher* than it sounds, because the 14% that differ mostly change by a line or two, not wholesale. On a codebase already formatted with Black, adopting ruff format produces a small, reviewable diff — not the all-files churn you'd fear from switching formatters.

What the 14% actually disagree on

The honest, useful part: I looked at every differing file, and none of the disagreements are bugs. They're cases where Black and Ruff make different — but equally valid — style choices. Two patterns covered almost all of them.

1. How a long `assert` message wraps. Black keeps the message on the assert line and wraps the condition; Ruff wraps the message into parentheses. Both are legal; Ruff's is the newer convention:

-  assert (
-      bool(static_host) == host_matching
-  ), "Invalid static_host/host_matching combination"
+  assert bool(static_host) == host_matching, (
+      "Invalid static_host/host_matching combination"
+  )
Real diff — flask/app.py (Black on top, ruff format below)

2. f-string quote normalization. When an f-string contains nested quotes, Ruff and Black pick opposite outer/inner quote pairings. Again — same string, different valid formatting:

-  f'{getattr(type_, "__module__", "")}.{getattr(type_, "__qualname__", "")}'
+  f"{getattr(type_, '__module__', '')}.{getattr(type_, '__qualname__', '')}"
Real diff — rich/_inspect.py

The rest were occasional blank-line handling at file edges. That's the whole story of the 14% — cosmetic tie-breaks, often because Ruff tracks the newest formatting conventions slightly ahead of Black. Nothing that changes behavior, nothing that should block a migration.

One practical note: because the two disagree on ~14% of files, don't mix them in one repo — pick one formatter and run it everywhere, or your CI will fight your editor. Migrate fully to ruff format in a single commit rather than running both.

Configuring the formatter

Like Black, ruff format is intentionally low-config — but it exposes the knobs that matter. The two you'll actually touch are quote style and line length. Both worked exactly as expected in my tests:

# default: double quotes
name = "world"

# with format.quote-style = "single"
name = 'world'
Real output — quote-style = single flips the quotes
# ruff format --config "line-length = 20"
result = some_function(
    argument_one,
    argument_two,
    argument_three,
)
Real output — line-length = 20 forces wrapping with a trailing comma

In a project you'd set these once in pyproject.toml under [tool.ruff] (line-length) and [tool.ruff.format] (quote-style, indent-style). That's the whole configuration surface most teams need — the point of an opinionated formatter is that you *don't* tune it endlessly.

Format Python with Ruff online (and migrating)

You don't have to install anything to try ruff format. Our Ruff formatter online runs the official Ruff WebAssembly build in your browser: paste code, switch to Format mode, set your line length and quote style, and see the reformatted output instantly — the same engine and output you'd get from ruff format locally, with nothing uploaded.

When you're ready to adopt it for real: install Ruff (pip install ruff or uv add ruff), set your line-length and quote-style in pyproject.toml, run ruff format . once to apply it across the repo in a single commit, then drop Black from your dev dependencies and point your pre-commit hook and CI at ruff format --check. Because it's 86% identical out of the box, the one-time reformat diff is small — and every format after that is ~29× faster.

Formatting is step one. Once your code is formatted, lint it with ruff check (see our Ruff vs Flake8 benchmark) and type-check it with our Python type checker — all three from the same fast, modern toolchain.

Format your Python with Ruff online — free

Paste your code into our Ruff formatter online, switch to Format mode, set your line length and quote style, and get Black-compatible output instantly. Official Ruff WebAssembly, nothing uploaded.

Open the Ruff Formatter

Free tools mentioned here

Frequently asked questions

Is ruff format the same as Black?

Nearly. In my test formatting 124 real files with both, 107 (86%) came out byte-identical to Black, and ruff format was about 29× faster (0.17s vs 4.86s). The 14% that differed were minor, valid style choices — how a long assert message wraps and how nested quotes in f-strings are normalized — not bugs. For most code they're indistinguishable, so ruff format is a practical drop-in replacement for Black.

Is ruff format faster than Black?

Yes, substantially. Formatting the same 124 Python files, ruff format took 0.17 seconds versus Black's 4.86 seconds — roughly 29× faster. Ruff is a native Rust binary while Black is a Python program, and the gap compounds across a large repository and on every save in your editor.

Should I switch from Black to ruff format?

For most projects, yes. It's 86% byte-identical to Black out of the box, ~29× faster, and lives in the same tool as your linter and import sorter. Migrate in a single commit: set line-length and quote-style in pyproject.toml, run ruff format . once to apply it, then remove Black and point CI at ruff format --check. Don't run both formatters in one repo, since they disagree on ~14% of files.

Can I format Python with Ruff online?

Yes. Our Ruff formatter online runs the official Ruff WebAssembly build in your browser — paste your code, switch to Format mode, choose your line length and quote style, and get the reformatted output instantly. It's the same engine and output as running ruff format locally, and nothing is uploaded because all processing happens on your machine.

Why does ruff format produce a different result than Black on some files?

Because on a minority of cases (about 14% of files in my test) the two formatters make different but equally valid style choices — mainly how a long assert statement's message is wrapped and how quotes inside f-strings are normalized. Ruff often tracks the newest formatting conventions slightly ahead of Black. Neither output is wrong; they're just different tie-breaks, so pick one formatter and use it consistently.

Can I configure ruff format like Black?

Yes, within the same opinionated, low-config philosophy. The main options are line-length (set under [tool.ruff] in pyproject.toml) and quote-style and indent-style (under [tool.ruff.format]). In my tests, setting quote-style to single flipped double quotes to single, and lowering line-length forced the expected wrapping with a magic trailing comma. Like Black, it's designed so you set a couple of options once and stop fiddling.

Keep reading