Ruff and ty: How One Company Quietly Rebuilt Python's Entire Dev Toolchain in Rust

A Python file used to pass through four separate tools before it was "clean." Now it passes through one — and it finishes before you blink. I've watched a lot of teams build their "code quality" stack like a Jenga tower: `flake8` for linting, plus a fistful of flake8 plugins, then `isort` for imports, then `black` for formatting, then `mypy` for types, all wired together with pre-commit hooks and a prayer. Every layer had its own config, its own quirks, its own ten-second pause in your feedback loop. Astral — the team behind `uv` — has spent the last two years quietly demolishing that tower and rebuilding it in Rust. Let me give you the modern version. ## Ruff: the one that already won `ruff` is a linter *and* formatter that replaces flake8 (plus dozens of its plugins), black, isort, pydocstyle, pyupgrade, autoflake, and more — with a single binary and **900+ built-in rules.** The speed is genuinely silly. The benchmark Astral publishes is the one that made me sit up: where flake8 takes about **30 seconds** to lint a 500,000-line Django codebase, Ruff does the same job in **under 0.3 seconds.** That's not "a bit faster," that's a different category of experience — Astral pegs Ruff at **10–100x faster** than the linters and formatters it replaces, and on a run like that it's at the top of that range. And this isn't a niche toy. Ruff has become one of the most widely adopted tools in modern Python — major projects like FastAPI, Pydantic, Airflow, Pandas, SciPy, and Hugging Face's Transformers all run on it. ### Setting it up ```bash uvx ruff check . # lint the whole project (no install needed) uvx ruff check --fix . # auto-fix everything safely fixable uvx ruff format . # format like black, but instant ``` Configure it entirely in `pyproject.toml`: ```toml [tool.ruff] line-length = 88 target-version = "py313" [tool.ruff.lint] # Start broad: pycodestyle (E), pyflakes (F), isort (I), # pyupgrade (UP), flake8-bugbear (B), comprehensions (C4) select = ["E", "F", "I", "UP", "B", "C4"] ignore = ["E501"] # let the formatter own line length ``` Those rule codes are families. `I` gives you isort. `UP` rewrites old syntax to modern Python automatically. `B` (bugbear) catches real footguns like mutable default arguments. You're configuring six former tools in six characters. The thing I love most: Ruff's autofix is good enough that I run `ruff check --fix` and `ruff format` on save, and most "code review nitpicks" simply never reach a human. That frees up review for things that actually matter — logic, naming, design. ## ty: the new kid finishing the set Linting and formatting were solved. The last slow, painful piece was *type checking*. `mypy` and Pyright are great, but on a big codebase the wait between "save" and "see the error" can be brutal — and that lag is exactly where type checking stops feeling helpful and starts feeling like a chore. Enter **`ty`** — Astral's type checker and language server, also in Rust, the third tool in the trio alongside ruff and uv. It's in **beta**, with a stable 1.0 targeted for 2026, and Astral now uses it exclusively on their own projects. The numbers Astral published with the beta are, again, faintly ridiculous: - Without caching, ty runs **10–60x faster** than mypy and Pyright. - ty checks the whole Django codebase in **~578ms**; Pyright takes **~16 seconds**. - For incremental edits — the thing you actually do all day — it's even wilder: after editing a load-bearing file in PyTorch, ty recomputes diagnostics in **4.7ms, about 80x faster than Pyright's 386ms.** That last one is the point. ty was architected from the ground up around *incrementality* — when you edit a function, it recomputes only what that change could affect, not the world. The result is type feedback that feels instant, the way a spell-checker feels instant. Types stop being a gate you run before commit and become a live conversation while you write. ```bash uvx ty check . # type-check your project, blazingly fast ``` It also ships as a language server, so your editor gets the same speed for hovers, go-to-definition, and inline errors. ### The honest catch with ty ty is beta, and it draws one firm line: **no plugin system.** If your project leans on mypy plugins — the Django ORM plugin, Pydantic v1, SQLAlchemy's mypy plugin — ty can't replace mypy *yet*, because those plugins teach the type checker about runtime magic ty doesn't model. For a modern, plugin-free codebase (especially Pydantic v2, which doesn't need a plugin), ty is already a joy. For a plugin-heavy legacy app, keep mypy for now and use ty for fast local feedback alongside it. It's worth noting ty isn't alone — Meta's `pyrefly` is another Rust-based type checker racing in the same lane. Competition here is *great* for us; it's why this space is improving monthly. ## Putting the whole trio together Here's the complete modern setup. One `pyproject.toml`, three tools, zero ceremony: ```toml [tool.ruff.lint] select = ["E", "F", "I", "UP", "B", "C4", "SIM"] [tool.ty.rules] # ty reads standard config; tune strictness here as it stabilizes ``` And a `.pre-commit-config.yaml` that runs in well under a second: ```yaml repos: - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.14.0 hooks: - id: ruff args: [--fix] - id: ruff-format ``` Locally, your loop becomes: ```bash uvx ruff format . # format uvx ruff check --fix . # lint + autofix uvx ty check . # type-check ``` Three commands, all Rust, all effectively instant. Notice they're all run with `uvx` — no installs polluting your project, courtesy of uv. The whole Astral story is one coherent idea: developer feedback should be *immediate*, because slow feedback is the thing that quietly erodes good habits. When linting takes 30 seconds, you skip it. When it takes 0.3, you never even notice it ran. ## Takeaways - **Ruff** replaces flake8 + isort + black + pyupgrade + more with one tool, 900+ rules, and Astral's stated 10–100x speedups (0.3s vs flake8's 30s on a 500k-line Django codebase). It's one of the most widely adopted tools in modern Python — adopt it today, full stop. - **ty** is Astral's Rust type checker: 10–60x faster than mypy/Pyright, ~80x on incremental edits, beta now, 1.0 targeted for 2026. - ty has **no plugins** — perfect for modern Pydantic-v2-era code, not yet a drop-in for plugin-heavy legacy apps. Keep mypy where you still need plugins. - Run all three via `uvx` for an instant, install-free `format → lint → type-check` loop. The meta-lesson: a huge chunk of "Python is slow tooling" was never about Python. It was about tools written *in* Python doing CPU-heavy work. Rewrite them in Rust and the friction evaporates. That's the whole Astral playbook — and it's working. --- *Want to set up a clean, fast lint + format + type-check pipeline on your project — or finally get your type hints under control? That's one of my favorite things to teach. I tutor 1:1 → /book-session.*