← Back to list

Typed Python 2025: Mypy + Rust Tools (Ty, Pyrefly) for Error-Free Codebases

For years, Python was the poster child of “move fast and don’t worry about types.” That worked when codebases were a few thousand lines and…

Er.Muruganantham in CodeToDeploy · 2025-12-07 13:15 · 69 claps · 7.6 min read paywalled
#python #rust #software-engineering #python-typing #static-analysis
Open on Medium ↗

Typed Python 2025: Mypy + Rust Tools (Ty, Pyrefly) for Error-Free Codebases

For years, Python was the poster child of “move fast and don’t worry about types.” That worked when codebases were a few thousand lines and lived in one team’s folder.

🚀 Top Remote Tech Jobs — $50–$120/hr

🔥 Multiple Roles Open — Limited slots! Hiring Experienced Talent (3+ years) Only.

  • Frontend / Backend / Full Stack
  • Mobile (iOS/Android)
  • AI / ML
  • DevOps & Cloud

*⏳ *Opportunities Fill FAST — Early Applicants Get Priority! 👉 [Apply Here](https://app.usebraintrust.com/r/code6/)**

Today, Python runs at the core of:

  • huge microservice systems
  • large monoliths at companies like Instagram and Meta
  • complex data platforms and ML infra
  • fintech and healthcare backends

In that world, “let’s just rely on tests” is not enough.

Static typing turned from a “nice-to-have” into infrastructure. And in 2025, a new generation of Rust-powered type checkers — especially Ty (by Astral) and Pyrefly (by Meta) — is changing how we do typed Python.

This article shows:

  • why typed Python is necessary now
  • where mypy still fits in
  • what Ty and Pyrefly do differently
  • concrete examples of type checking and errors
  • how to plug these tools into a real workflow
  • where to go deeper with official sources

1. Why Typed Python Matters in 2025

Dynamic typing is fantastic for:

  • quick scripts
  • one-off automation
  • experiments

But in a long-lived, multi-developer codebase, it causes real pain:

  • A function that used to return str now sometimes returns None, and someone finds out only at runtime.
  • A data structure slowly changes shape (dictionary → object → another object), and half the call sites implicitly break.
  • A refactor removes a parameter, but 15 other modules still call the old signature.
  • One team expects User to have email: str, another thinks it can be None, a third expects a nested object.

Static typing solves a simple but critical problem:

“Can I change this code without breaking everything else?”

Types give you contracts and compile-time feedback.

  • IDEs become smarter (jump to definition, refactors, autocomplete).
  • Large refactors become safer.
  • Cross-team collaboration is easier because interfaces are explicit.

In other words: typing is less about being strict and more about being able to change fast without fear.

2. Mypy: The Workhorse That Built Typed Python

If you’ve worked with typed Python, you’ve probably used mypy.

It did three important things for the ecosystem:

  1. Proved that static typing for Python was practical.
  2. Shaped a lot of typing semantics (PEP 484 and beyond).
  3. Became the default type checker for many projects.

Simple mypy example

Consider this simple function:

# file: math_utils.py
def add(a, b):
    return a + b

This runs fine. But we don’t know:

  • whether a and b are integers, floats, strings, or something else
  • what type the function returns

Add explicit types:

# file: math_utils.py
from typing import Union

Number = Union[int, float]

def add(a: Number, b: Number) -> Number:
    return a + b

Now run mypy:

mypy math_utils.py

If some other file uses it incorrectly:

# file: bad_usage.py
from math_utils import add

result = add("10", 5)  # mixing str and int

mypy will report a type error before runtime.

This is already a big win — but as codebases scale into hundreds of thousands or millions of lines, mypy can start to feel slow.

That’s where Rust-based tools come in.

3. Rust Enters the Chat: Why Ty and Pyrefly Exist

Type checking is computationally heavy:

  • The checker must parse a lot of files.
  • It must track types through imports, inheritance, generics, async flows.
  • On big repos, this means thousands of files and complex graphs.

Python itself is not the best language to write that kind of high-performance tool. Rust is.

Rust gives type checkers:

  • near-native performance
  • memory safety (the tool itself is less likely to crash)
  • easy multi-threading and parallelism

That’s why you see a pattern now:

  • ruff (linter/formatter) → Rust
  • uv (package & env manager) → Rust
  • Ty → Rust
  • Pyrefly → Rust

The idea is simple: keep Python as the language, but use Rust as the engine behind the tooling.

4. Ty: “mypy, But Fast” for Modern Python Projects

Ty is built by Astral, the team behind Ruff and uv. It is a Rust-based static type checker and language server for Python.

The core goal of Ty:

“Give you mypy-like checking at a fraction of the time, so you actually keep typing turned on.”

Installing Ty

If you’re using uv:

uvx ty check

Or via pip:

pip install ty
ty check src/

The check command is the main entry point: it scans your project, reads your type hints, and reports errors.

Example: catching a subtle bug with Ty

Imagine a service layer:

# file: service.py
from typing import TypedDict

class User(TypedDict):
    id: int
    email: str

def get_email(user: User) -> str:
    return user["email"]

Later, someone decides emails can be optional:

class User(TypedDict, total=False):
    id: int
    email: str

Now email might not exist on a given User. Type checker view:

def get_email(user: User) -> str:
    return user["email"]  # unsafe now

Ty (like mypy) will now flag:

  • either that email may be missing
  • or that the return type may not always be str

You can then fix it properly:

from typing import Optional

def get_email(user: User) -> Optional[str]:
    return user.get("email")

Ty doesn’t add new type rules here; it just checks them much faster, so you can afford to run it often — in CI and even in pre-commit hooks.

5. Pyrefly: Meta’s Rust-Based Type Checker and Language Server

Pyrefly is Meta’s successor to Pyre: a new type checker and language server written in Rust, designed for large-scale codebases like Instagram’s.

Where Ty focuses on speed and simplicity, Pyrefly also focuses heavily on:

  • deep IDE integration
  • code navigation
  • strictness and correctness for huge repos

You can think of it as:

“A proper compile-time engine for Python, with the UX of a modern language server.”

Installing Pyrefly

pip install pyrefly
pyrefly check src/

For editor use (VS Code, etc.), it also exposes a language server that powers:

  • instant error underlines
  • go-to-definition
  • find references
  • rename symbols
  • semantic highlighting

Example: strict checking in Pyrefly

Suppose you have a function:

def find_user_email(user_id: int) -> str:
    user = fetch_user(user_id)
    if not user:
        return None
    return user.email

This is common in dynamic Python — returning None when something isn’t found—but your type hint says -> str.

A strict checker like Pyrefly will complain:

  • “You said you return str, but you returned None along some paths.”

You then correct the function:

from typing import Optional

def find_user_email(user_id: int) -> Optional[str]:
    user = fetch_user(user_id)
    if not user:
        return None
    return user.email

This seems small, but across a large codebase, these small mismatches are exactly what lead to runtime bugs.

6. Putting It All Together: A Typed Python Workflow in 2025

Here’s a realistic way to bring typed Python plus Rust tooling into an existing project.

Step 1: Turn on typing gradually

Start with your public interfaces:

# file: api.py
from typing import Any

def create_user(data: dict[str, Any]) -> dict[str, Any]:
    ...

Then tighten types over time:

from typing import TypedDict

class CreateUserPayload(TypedDict):
    email: str
    name: str

class User(TypedDict):
    id: int
    email: str
    name: str

def create_user(data: CreateUserPayload) -> User:
    ...

Now a type checker knows exactly what goes in and comes out.

Step 2: Keep using mypy if you already have it

If your team already relies on mypy, don’t throw it away. It’s still great as a “truth baseline.”

You can:

  • keep mypy in CI initially
  • experiment with Ty or Pyrefly locally
  • compare speed and diagnostics

Step 3: Drop in Ty for faster feedback

Replace or complement your mypy step:

ty check src/

You’ll likely notice:

  • full-project checks are much faster
  • incremental runs on changed files feel almost instant

This makes it realistic to:

  • run type checks in every PR
  • enforce type cleanliness for new code
  • treat type errors as CI blockers

Step 4: Add Pyrefly where you care about strictness and IDE experience

If you work on:

  • a central platform library
  • critical service boundaries
  • data pipelines that must not silently break

Set up Pyrefly:

pyrefly check src/

Then:

  • configure your IDE to use the Pyrefly language server
  • start using go-to-definition, rename, references, etc.
  • rely on its diagnostics while editing

For teams at scale, this brings Python closer to the experience of working with TypeScript or Rust: the editor constantly tells you where things do not match your contracts.

7. Code Example: Full Mini-Flow With Types + Checker

Let’s write a small but realistic mini-flow: create a user, store them, and fetch them.

Domain types

# types.py
from typing import TypedDict

class CreateUserPayload(TypedDict):
    email: str
    name: str

class User(TypedDict):
    id: int
    email: str
    name: str

Repository layer

# repo.py
from typing import Optional
from .types import CreateUserPayload, User

_db: dict[int, User] = {}
_next_id = 1

def create_user(payload: CreateUserPayload) -> User:
    global _next_id
    user: User = {
        "id": _next_id,
        "email": payload["email"],
        "name": payload["name"],
    }
    _db[_next_id] = user
    _next_id += 1
    return user

def get_user(user_id: int) -> Optional[User]:
    return _db.get(user_id)

Service layer

# service.py
from typing import Optional
from .types import CreateUserPayload, User
from .repo import create_user, get_user

def register_user(payload: CreateUserPayload) -> User:
    # Could add validations here.
    return create_user(payload)

def get_user_email(user_id: int) -> Optional[str]:
    user = get_user(user_id)
    if not user:
        return None
    return user["email"]

Where type checking helps

  1. If someone later “simplifies” CreateUserPayload to allow missing email and forgets to update create_user, Ty or Pyrefly will flag that you’re accessing payload["email"] assuming it exists.
  2. If someone changes get_user to return User instead of Optional[User] but leaves the service logic unchanged, the checker will warn about redundant if not user checks or mismatched return types.
  3. If a caller does:
# elsewhere.py
from .service import register_user

user = register_user({"name": "Alice"})  # missing email
  1. The type checker can flag that this dict does not match CreateUserPayload.

In a dynamic world, all of this compiles and fails only at runtime. With strong typing plus a fast checker, you catch it before deploy.

8. How This Changes Your Role as a Python Developer

When you embrace typed Python with fast checkers:

  • You spend less time on “stupid” bugs (wrong key, wrong type, missing field).
  • Refactors become safer and more frequent.
  • Reviews focus more on architecture and logic, not basic correctness.
  • Junior developers are guided by the type system, not just code review comments.

Python starts to feel like a high-level language with a real safety net.

You still get:

  • expressiveness
  • readability
  • fast iteration

But now with a Rust-powered guardian watching your contracts.

References and Further Reading

These are good starting points to dive into each tool and concept mentioned above:

  • Astral’s announcement and documentation for Ty, a Rust-based Python type checker and language server.
  • Astral’s ecosystem overview explaining how Ruff, uv, and Ty fit together for modern Python projects.
  • Meta’s engineering posts and official site for Pyrefly, the Rust-powered successor to Pyre, designed to scale to Instagram-sized codebases.
  • Pyrefly’s documentation for IDE integration, language-server features, and strict typing modes.
  • Comparative articles and community write-ups discussing Ty vs Pyrefly and the broader trend of Rust-based Python tooling.

Thank you for being a part of the community

Before you go:

👉 Be sure to clap and follow the writer ️👏️️

👉 Follow us: **X | [Medium](https://medium.com/codetodeploy)**

👉 CodeToDeploy Tech Community is live on Discord — **Join now!**

👉 Follow our publication, CodeToDeploy

Note: This Post may contain affiliate links.


메타데이터
post_id
641f874d5a9f
slug
typed-python-2025-mypy-rust-tools-ty-pyrefly-for-error-free-codebases-641f874d5a9f
url
https://medium.com/codetodeploy/typed-python-2025-mypy-rust-tools-ty-pyrefly-for-error-free-codebases-641f874d5a9f
canonical_url
https://medium.com/codetodeploy/typed-python-2025-mypy-rust-tools-ty-pyrefly-for-error-free-codebases-641f874d5a9f
author_url
https://medium.com/@muruganantham52524
status
ok
fetched_at
2026-07-13 12:56:55