Python Type Checking: mypy vs Pyright Performance Battle
Two type checkers promise faster Python. One might be wrong for your workflow.
Python Type Checking: mypy vs Pyright Performance Battle
Two type checkers promise faster Python. One might be wrong for your workflow.

Mypy vs Pyright isn’t about who’s “right” it’s about which type checker is fast enough to actually run every time you code.
Your test suite passes. Green checks everywhere. The code ships to production, and three hours later you’re staring at a TypeError on line 847 because a function that's supposed to return a dictionary is sometimes returning None, and now you're debugging at 11 PM with a cold cup of coffee and a growing sense that you've made poor life choices.
You know type hints would have caught this. You’ve known for months, actually. But you’ve heard mypy is painfully slow, and someone on your team won’t shut up about Pyright, and honestly you just want to stop seeing NoneType has no attribute errors when you're trying to sleep.
You’re not alone in this. A 2025 survey found that 73% of Python developers use type hints in production code now, but only 41% actually run type checkers in CI because of speed concerns and integration friction. The weird thing is, both mypy and Pyright actually solve the problem. Like, they both work. The real question isn’t which one catches bugs better, it’s which one fits the way you actually write code without making you want to disable it after a week.
Here’s what actually matters when you stop overthinking it.
The Thing Everyone Gets Wrong About This
So mypy came first, right? It’s the reference implementation. Guido gave it his blessing. It feels official in this vague, institutional way. And it’s written in Python, which sounds like it should be perfect because you’re checking Python with Python, but that also means it’s parsing Python… with Python. Which gets real slow real fast on anything bigger than a Flask tutorial.
Pyright showed up later, written in TypeScript, running on Node. Microsoft built it specifically for VSCode, and that already tells you everything about what they cared about. It needs to be fast enough to check your code while you’re actively typing. Real time feedback. Zero lag. That immediate red squiggle under your mistake before you even hit save.
And here’s the part that took me forever to understand. Python type checking isn’t just about correctness in some abstract, academic sense. It’s about whether the checker becomes invisible or whether it becomes this annoying thing you wait for. Because if mypy takes 30 seconds every time you run it, you stop running it locally. You shove it into CI. And then you don’t see the errors until after you’ve already committed and moved on to the next feature and your brain has completely context switched.
Actually, wait. The speed difference isn’t even the real story here.
The Speed Thing Is Messier Than Anyone Admits
Pyright is faster. Like, significantly faster. On a 50,000 line codebase, mypy might chew through it in 15 to 20 seconds on a cold run. Pyright does the same thing in 3 or 4 seconds. That’s not some cherry picked benchmark, that’s just what happens in normal projects.
But mypy has this daemon mode. You run dmypy run instead of regular mypy, and it keeps a background process alive that caches everything. Type information, module structure, all of it. Subsequent runs drop down to 2 or 3 seconds. Suddenly that massive gap basically disappears.
So if you’re running checks in CI, Pyright wins hands down because CI runs are always cold. Fresh container, no cache, start from zero every time. If you’re running checks locally in a loop while you code, mypy daemon mode keeps up just fine. And if you’re already using VSCode, Pyright is running in the background through Pylance anyway, so you’re getting type errors as little red squiggles before you even think about running a command.
The pattern that emerges: cold runs favor Pyright, cached incremental runs favor mypy daemon, editor integration overwhelmingly favors Pyright because that’s literally what it was built for.
You can test this right now, actually. Install both with pip install mypy pyright, then time them on your codebase. time mypy . versus time pyright .. Just see which one makes you impatient enough to go check your phone while it runs.
When They Disagree About What’s Wrong
Okay so here’s where it gets genuinely weird. They don’t always agree on what counts as a type error. Pyright is stricter by default, like noticeably stricter. It catches things mypy just lets slide, especially around None handling and those fuzzy boundaries where typed code meets untyped code.
Quick example. You’ve got a function that returns Optional[str], and somewhere else you call .upper() on the result without checking if it's None first. Pyright immediately yells at you. Red squiggle, angry message, the whole thing. Mypy might just let it pass depending on your config settings.
This isn’t a bug in either tool. It’s a philosophical difference about what type checking should even do. Mypy’s default position is kind of like “trust the developer, flag the obviously broken stuff.” Pyright’s default is more like “catch absolutely everything that could possibly fail at runtime, even if it’s unlikely.”
You can configure both to be stricter or more permissive, but defaults matter enormously because most people never change them. I certainly didn’t for the first six months.
If you’re trying to add types to a legacy codebase that’s been around since Python 2.7 and has maybe 15% type coverage, mypy’s permissiveness is actually helpful. You can add types gradually, one module at a time, without fixing every single warning in the entire codebase. If you’re starting a fresh project or you want maximum safety and you don’t mind the noise, Pyright’s strictness will save you from yourself repeatedly.
Neither approach is wrong, exactly.
What Actually Determines Your Choice
A year from now, let’s say your codebase has 80% type coverage. You’re past that initial “should we even bother with this” phase. You’re just running the type checker, fixing what it finds, moving on. It’s part of the routine.
If you live in VSCode, you’re probably already using Pyright without even realizing it. Pylance is Pyright. The integration is so smooth it’s basically invisible. Errors just appear inline as you type. You don’t run a separate command. The editor knows.
If you’re setting up CI pipelines, Pyright is faster and the error output is genuinely better. The JSON format is cleaner. Exit codes make more sense. It’s designed to be machine readable first, which matters when you’re parsing output in a GitHub Action.
If you need to extend the type checker with custom plugins for your weird internal framework, mypy wins because it’s Python all the way down. You can write a plugin in an afternoon with regular Python code. Pyright has a plugin system but it’s TypeScript, and that’s a whole context switch most Python developers don’t want to deal with.
The pattern most teams eventually land on looks something like this. You start with Pyright for the speed and the editor integration. You switch to mypy if you hit a wall with gradual adoption on a messy legacy codebase or if you need custom validation rules. Or you run both in CI if you’re paranoid and cloud compute is cheap enough.
Circle back to that 11 PM debugging session. The type checker that actually prevents the bug is the one you actually run. Locally. Before you commit. Without having to remember to do it.
Conclusion
Python type checking isn’t about objectively picking the better tool. It’s about picking the one that vanishes into your workflow instead of fighting it. Both mypy and Pyright catch type errors. The difference is speed, strictness levels, and whether the tool constantly reminds you it exists or just quietly does its job.
Pyright is faster on cold runs and stricter by default. Mypy handles gradual typing better and has a richer plugin ecosystem. Both beat spending your evenings debugging None errors in production logs.
The payoff is specific and honestly pretty modest. Fewer runtime type errors. Clearer function signatures. Better autocomplete. You run the checker, you fix the warnings, you ship code that breaks less often in stupid preventable ways.
Which one matches the way you actually work instead of the way you think you should work?
Follow me for more such content.
메타데이터
- post_id
- fce38c8cb874
- slug
- python-type-checking-mypy-vs-pyright-performance-battle-fce38c8cb874
- url
- https://medium.com/@ashusk_1790/python-type-checking-mypy-vs-pyright-performance-battle-fce38c8cb874
- canonical_url
- https://medium.com/@ashusk_1790/python-type-checking-mypy-vs-pyright-performance-battle-fce38c8cb874
- author_url
- https://medium.com/@ashusk_1790
- status
- ok
- fetched_at
- 2026-06-20 20:29:01