Open-Sourcing Pyrefly: A Faster Python Type Checker Written in Rust

Meta open-sources Pyrefly, a Python type checker built in Rust that grew out of Instagram's internal tooling and is designed around an incremental model fast enough for real-time IDE feedback at monorepo scale.

Open-Sourcing Pyrefly: A Faster Python Type Checker Written in Rust

The common story about rewriting a tool in Rust is about speed. Pyrefly is faster than Mypy, but speed is not why Meta built a new type checker from scratch. The predecessor, Pyre, was already written in a fast language — OCaml — and it still couldn’t be made incremental enough for real-time IDE integration. The real problem was architectural, and Rust was the path to fixing the architecture.

Source

Open-sourcing Pyrefly: A faster Python type checker written in Rust

By: Meta
Published: May 15 2025
Type: Engineering article

Saved because the design rationale — starting from incremental checking as a first-class requirement rather than a post-hoc optimization — is the kind of architectural decision that determines whether a type checker can serve large codebases, and the Python ecosystem has needed a credible answer to this for years.

How Pyrefly became what Pyre couldn’t be

Meta’s original Python type checker, Pyre, was built in OCaml starting around 2016 and open-sourced later. It served Instagram’s large typed Python codebase for years and worked well enough at that task. But “worked well enough” meant a batch-oriented architecture that checked entire files or module groups at a time. When the team identified two specific goals — sub-100ms IDE feedback and file-granular incremental checking — it became clear that neither was achievable by extending Pyre. The architecture wasn’t wrong; it just wasn’t designed for those targets.

Pyrefly’s lineage starts in 2017 as an internal tool for Instagram’s typed Python codebase, when Instagram had already made a substantial investment in PEP 484 annotations at a time when this was still unusual in large Python projects. The checker that emerged from that work eventually became the foundation for Pyrefly: a general-purpose checker designed from the start around incremental evaluation.

The incremental model is the central engineering decision. At any point, Pyrefly maintains a graph of modules and their type-level dependencies. When a file changes, the checker recomputes that file’s type signature export — what types flow out of the module via its public interface. If the interface signature is unchanged (an internal implementation detail changed but nothing callers see changed), no downstream module needs re-checking. If the signature does change, only modules that directly import from it are invalidated. This propagation is bounded by the actual dependency structure, not the full codebase size. On a monorepo with tens of millions of lines of Python, this distinction is the difference between checking completing in milliseconds and in minutes.

The Rust choice makes this tractable in a specific way. Rust’s ownership and borrowing model makes explicit exactly when data can be shared, mutated, or invalidated. In a system where the whole point is to track which cached type computations are no longer valid when a file changes, that explicitness is genuinely useful rather than just a safety property. The alternative — building the same invalidation logic in a garbage-collected language — requires careful discipline to avoid holding stale references in caches. Rust makes the discipline structural.

IDE integration surfaces the incremental model directly. When a developer types in an editor, edits arrive as a stream of changes. Pyrefly applies each edit to an in-memory representation of the current module, the incremental checker determines which type computations are affected, and updated diagnostics are pushed back to the editor. The stated target is sub-100ms from edit to diagnostic update. That’s a much more demanding target than “runs fast in CI” — it requires the hot path from edit to result to touch as little state as possible, which is exactly what the dependency graph and signature-based invalidation enable.

One design choice that avoids a common problem: the CLI and IDE modes share the same underlying type-checking engine. Some tools split their implementations — a fast approximate checker for the IDE and a complete checker for CI — which leads to behavior divergence where an error appears in CI but not in the editor. Pyrefly avoids this by design: the check you see in the editor is the same check that runs in CI. The only difference is how results are surfaced.

The open-source release on May 15, 2025 positions Pyrefly as a community tool rather than a Meta-internal project. The predecessor Pyre remains available. Ruff, written in Rust by Astral rather than Meta, covers the linting and formatting space adjacent to type checking. The interesting question for the Python tooling ecosystem is whether Pyrefly’s incremental model creates a new baseline expectation — similar to how TypeScript’s language server set expectations for JavaScript IDE responsiveness — and whether that pulls other Python type checkers in the same direction.

The contribution surface question matters here. Most Python developers who want to contribute to type-checking tooling know Python, not Rust. Open-sourcing a Rust codebase to a Python-fluent community is a real accessibility barrier. Meta has acknowledged this and is actively working to document the codebase for new contributors, but it remains a genuine tradeoff: higher performance ceiling, higher contribution barrier.

My take

The thing to watch is whether Pyrefly makes real-time IDE feedback a standard expectation for Python type checkers rather than a nice-to-have. TypeScript’s language server created that expectation for JavaScript 10 years ago, and it permanently changed how developers think about type-annotated JavaScript. Python has been slow to reach that bar because the Python type checking ecosystem never had a checker architected around it.

Where this is useful

  • Large Python monorepos where Mypy’s checking time makes running it in CI painful or where re-checking on every save is impractical
  • IDE-first development workflows where sub-100ms feedback changes how much you rely on the type checker during coding rather than only during review
  • Teams evaluating type checkers who need CLI and IDE behavior to be identical — no surprises where CI catches something the editor didn’t show
  • When NOT applicable: small codebases where Mypy or Pyright already return results fast enough that the performance argument doesn’t land; the Rust contribution barrier may outweigh the incremental checking benefit

Apply this: Before adopting Pyrefly, profile what percentage of your CI type-checking time is Mypy — if it’s under a minute, the migration cost probably doesn’t pay off yet; if it’s tens of minutes, the incremental model is the thing to test first.

What I want to remember

Remember: Incremental type checking works by tracking module interface signatures, not full module content — if a file’s public types don’t change, none of its dependents need re-checking, which is what makes it scale to monorepos.