Google Just Switched Its Python Type Checker to Pyrefly — Your ML Pipeline Should Pay Attention

You know that nagging 60-second wait every time mypy runs on your ML service? Google felt it too — at a scale of hundreds of millions of lines of Python. This week they published exactly why they're migrating that entire codebase to Pyrefly, and the numbers are the kind that make you rethink your whole Python toolchain setup.
If you're a MERN developer who also maintains a Python service for AI inference, data processing, or some FastAPI endpoint tacked onto your Express backend — this one is directly relevant to you.
I've been using Ruff for a while now for Python linting and formatting, and it honestly changed how I felt about Python tooling. Fast, zero-config, and it just works. Pyrefly is giving me the same vibes for type checking — I haven't fully committed yet, but it's been sitting on my "actually try this on a real project" list for a couple weeks now.
The Problem With Python Type Checking Nobody Likes to Admit
Python's type system is a weird beast. You get the flexibility of a dynamic language but you also have the option to add type hints, and theoretically a type checker catches your mistakes before they become 2am incidents.
The catch? mypy — the reference implementation — is written in Python, and it shows. On a moderately-sized codebase it can take upwards of a minute for a full run. For a tight edit-save-test loop, that's basically unusable. Most developers do one of two things: they skip type checking entirely, or they run it only in CI and quietly ignore the warnings for months.
Neither is great.
Google was running Pytype, their own internal type checker. It worked by analyzing compiled bytecode rather than source, which let it infer types even in code with no annotations at all. Clever. But the bytecode approach also meant every new Python version broke things, and it just couldn't keep pace with how fast the typing PEPs were shipping. Python 3.12 is the last version Pytype supports.
They needed something different.
What Is Pyrefly, and Why Is Rust Involved?
Pyrefly is a Python type checker built by Meta, written in Rust, and it hit 1.0 in May 2026. If you've been following the tooling landscape, you've probably noticed a pattern: the tools that developers use daily are increasingly being rewritten in Rust for performance reasons. TypeScript's compiler is getting a Go rewrite. Ruff replaced flake8 and isort for Python linting. ESLint's new architecture is borrowing ideas from Rust's ownership model.
Pyrefly is that same story for type checking.
A few things make it stand out:
It's parallel and lazy. Pyrefly doesn't type-check your whole codebase upfront. It evaluates lazily and distributes the work across cores, which means even cold starts are fast.
It has a first-party LSP. You can wire it into VS Code or Neovim today and get inline type errors on save without polling a slow background process.
It scores 97% on the typing conformance suite. That's the official test suite that checks whether a type checker is implementing Python's typing specs correctly. mypy isn't that far off, but the gap matters when you're working at Google scale.
One thing that trips people up on migration: Pyrefly is stricter about union types than most checkers. If you have a function typed def greet(name: str) -> str and somewhere you're passing it str | None, Pyrefly will flag that. mypy might not. That's not Pyrefly being annoying — that's it being correct.
# This will pass mypy with default settings
def process_user_id(user_id: str) -> str:
return user_id.upper()
user_input: str | None = get_user_input()
process_user_id(user_input) # ❌ Pyrefly catches this. mypy doesn't.
# The fix Pyrefly is nudging you toward:
if user_input is not None:
process_user_id(user_input) # ✅
That kind of strictness catches bugs. Especially in ML code where None values sneak in from model outputs, empty datasets, or missing config keys.
The Numbers That Made Google Move
Here's what Google published from their internal benchmarks after migrating:
Up to 98% lower latency for incremental edit-and-rebuild cycles
90–99% reduction in type-checking's share of clean build time
80%+ reduction in daily peak compute just for Python type checking
In raw terms: for one large internal target, Pytype was taking between 2.7 seconds and 246.7 seconds for edit-and-rebuild, depending on the target size. Pyrefly ran the same workload in 1.7 to 4.5 seconds.
Independent benchmarks (not Google's numbers) tell a similar story. A June 2026 analysis on a medium-sized open-source Python codebase found Pyrefly was about 14x faster than mypy. ty (OpenAI's Rust type checker, still in beta) was around 17x — but Pyrefly has the edge in IDE integration right now.
A 60-second mypy run becomes roughly 4 seconds with Pyrefly. That's the difference between "I'll just skip type checking locally" and "I'll run this on every save."
What This Means If You're a MERN Dev With Python in the Mix
Here's the angle that's worth thinking about: a lot of MERN developers aren't running pure JavaScript/TypeScript stacks anymore. The typical setup I see on projects is something like this — Express backend serving the main API, MongoDB for data, React on the front, and then there's a Python service tucked in the corner doing the ML inference, the embeddings generation, or the data pipeline work.
That Python service is probably the least typed part of your stack. Which is ironic, because it's often the part that processes the least predictable data — user inputs, model outputs, third-party API responses.
Pyrefly makes it genuinely practical to add type checking to that service without it becoming a CI bottleneck. With mypy, type checking a moderately complex FastAPI or Flask service adds real time to every PR check. With Pyrefly, it's a rounding error.
Ruff, mypy, and Pyrefly: Where Each Tool Fits
This is a comparison I've seen a lot of confusion about, especially if you came from the linting side of things first (like I did with Ruff). Here's the thing — these tools aren't actually competing with each other. They handle different jobs.
Ruff is a linter and formatter. It catches style issues, unused imports, bad patterns, enforces formatting — all the stuff flake8, isort, and black used to do, but written in Rust so it runs in milliseconds. Ruff does not check types. It has no idea if you're passing str | None where a str is expected.
mypy and Pyrefly are type checkers. They read your type hints and verify your code is actually using types correctly. They don't care about formatting or unused imports.
So in a modern Python project, you'd typically run both: Ruff for linting/formatting, and either mypy or Pyrefly for type checking. The question isn't "which one do I pick" — it's "which type checker do I pair with Ruff."
Here's how the full picture looks:
| Ruff | mypy | Pyrefly | ty (beta) | |
|---|---|---|---|---|
| What it does | Linting + formatting | Type checking | Type checking | Type checking |
| Written in | Rust | Python | Rust | Rust |
| Speed | ~100× faster than flake8 | Baseline (slow) | ~14× faster than mypy | ~17× faster than mypy |
| Editor integration | ✅ First-party LSP | ✅ Mature | ✅ First-party LSP | ❌ CLI only (for now) |
| Conformance score | N/A | ~96% | ~97% | ~95% |
| Union strictness | N/A | Permissive | Strict | Strict |
| Made by | Astral | Open source | Meta | OpenAI |
| Production ready | ✅ Yes | ✅ Yes | ✅ Yes (v1.0) | ⚠️ Beta |
| Use it when | Always — replaces flake8/black | Legacy projects, broad ecosystem | New projects, CI bottleneck pain | Max speed matters, editor not required |
The setup I'd reach for on a new project today:
# Install both — they're complementary
pip install ruff pyrefly
# Ruff handles lint + format
ruff check app/
ruff format app/
# Pyrefly handles types
pyrefly check app/
# pyproject.toml
[tool.ruff]
line-length = 88
select = ["E", "F", "I", "UP"] # errors, pyflakes, isort, pyupgrade
[tool.pyrefly]
search_path = ["app", "tests"]
python_version = "3.12"
If you're already on Ruff (which, honestly, you should be by now), adding Pyrefly is the natural next step to close the type-safety gap. The two tools together take maybe 5–8 seconds total on a medium-sized codebase. That's fast enough to run in a pre-commit hook without anyone complaining.
One honest caveat: if you have a large existing codebase with zero type hints, don't add Pyrefly and expect a clean run. You'll get hundreds of errors. The move there is to add types incrementally — one module at a time — and only run Pyrefly on the annotated parts in CI until coverage improves.
How to Actually Start Using Pyrefly
Installation is a single pip command:
pip install pyrefly
Running it is similar to mypy:
# Check a file
pyrefly check app/models.py
# Check a whole directory
pyrefly check app/
# Watch mode (runs on save)
pyrefly check --watch app/
For VS Code, install the Pyrefly extension and it'll use the binary you just installed. No separate language server process to manage.
Migration strategy if you're on mypy:
The pragmatic approach is don't rip out mypy on day one. Run both in CI for a sprint. Pyrefly will surface errors mypy missed — most of them will be real issues, not false positives. Address them incrementally. Once Pyrefly's been clean for a few weeks, drop mypy.
# In your CI pipeline (temporarily run both)
mypy app/ --ignore-missing-imports
pyrefly check app/
# pyproject.toml config for Pyrefly
[tool.pyrefly]
search_path = ["app", "tests"]
python_version = "3.12"
One gotcha: if you're using # type: ignore comments liberally, Pyrefly handles them, but the syntax for ignoring specific codes is slightly different from mypy's. Worth auditing before you cut over fully.
The Bigger Picture
There's a pattern here that's worth naming. The tooling you use shapes how you think about code quality. When type checking is slow, you defer it. When it's fast enough to run on every save, it becomes part of your editing loop — the same way TypeScript caught on because the VS Code integration made errors immediate rather than deferred to a CI run.
Python's had that gap for a while. Fast Rust-based checkers are closing it.
Google adopting Pyrefly at scale is the kind of signal that moves the ecosystem. It's not an endorsement by a startup — it's a company with one of the largest Python codebases in the world saying "this is where we're going." The Python tooling ecosystem tends to consolidate around what Google, Meta, and the core team align on. That alignment is happening now.
If you've been meaning to add proper type checking to your Python service but kept putting it off because mypy was too slow to fit into your workflow — this is a good week to take another look.
I'm personally still on the Ruff + mypy combo for now, but Pyrefly is next on my list to actually test on a real project — not a toy repo. If you've already made the switch, especially from mypy, drop a comment below. Curious how many type: ignore comments you had to clean up and whether the stricter union checking was a headache or a relief.
About the author
- Love to code, gaming. And I use vim btw.
Comments
Join the discussionNo comments yet. Be the first to comment.





