Years ago I made a decision I was quietly embarrassed about: I moved our Psalm job out of the pre-commit hook and into CI only, because nobody on the team would sit through a 50 second wait every time they saved a typo fix. It felt like lowering the bar. Then I watched the same thing happen on three other teams, and I stopped feeling guilty and started feeling like the tool and the workflow simply didn't fit. That's the itch Jorg Sowa says he was scratching when he started mir in April 2026, and the numbers he published are hard to ignore.

Quick facts, because they matter. mir is written in Rust, MIT licensed, currently at version 0.88. On one laptop it analyzes Symfony and Laravel in 0.7 to 2 seconds; Psalm took 37 to 54 seconds and peaked at 3 to 4 GB of memory across its main process and nine workers. Sowa reports a range of 19 to 82 times faster with a fifth of the memory or less. The Symfony run used commit b47221ac with Symfony's own untouched psalm.xml at error level 5 plus its baseline; the Laravel run covered 1,742 files of v13.36.0. He's upfront that Psalm's --diff mode, which only rechecks changed files, wasn't part of the comparison, and I'd love to see that column filled in before anyone treats the 82x as gospel.

But here's what I keep coming back to, and it isn't the stopwatch. mir doesn't ship its own type language. It reads psalm.xml when there's no mir.xml, picks up psalm-baseline.xml on its own, honors errorLevel 1 through 8 with Psalm's per-issue mapping, and accepts @psalm-pure, @psalm-immutable, @psalm-suppress and the rest, plus PHPStan syntax and @phpstan-ignore. Issue names like InvalidArgument and TaintedSql stay the same where both tools have the check. In other words, the thing that started at Vimeo as one project's docblock dialect has become an interface that a completely separate engine can implement. That's a huge compliment to the PHP community. We built a richer type system than the language itself offered, in comments, and it turned out sturdy enough to outlive any single implementation of it.

And that's exactly why I think the next step is boring and overdue: we need a written specification for the docblock type language. Right now what list<T>, non-empty-string or a literal-type array shape means in an edge case is defined by whatever Psalm does, mostly agreed with by PHPStan, and since PhpStorm 2020.3, roughly understood by your IDE. With a third serious interpreter in the mix, "whatever Psalm does" stops being good enough. Sowa himself warns that your first mir run will produce a different issue list, that not every rule exists yet and some names differ. Some of those gaps are just unfinished work. Some will be genuine disagreements about what a type means, and without a spec, nobody can say who's right.

The strongest objection to all this enthusiasm is one I take seriously. Psalm is written in PHP, which means the people who use it can read it, debug it and send a patch on a Sunday afternoon. A Rust analyzer moves the engine out of reach for most of its users. Plugins show the cost clearly: mir has a native Rust plugin API modeled on Psalm's event handlers, and existing PHP plugins like the PHPUnit one only work through a bridge that starts a PHP subprocess, which still needs PHP and Psalm installed. If the community's analysis tooling drifts toward a smaller pool of maintainers in another language, that's a real loss in resilience, and I won't pretend otherwise.

Still, I land on the side of welcoming it, for a practical reason. Your investment was never in Psalm's source code. It lives in the annotations your team wrote over years, the baseline you've been chipping away at, the suppressions with comments explaining why. Because mir treats all of that as the contract, trying it costs you a composer require and one command, and you can run both analyzers side by side in CI until you trust the output. Nothing gets rewritten. If mir stalls at 0.9 forever, you uninstall it and lose an afternoon. Go developers got their fast linters by rewriting tools and asking everyone to adopt new conventions; we get to keep ours and swap the engine underneath, which is frankly the nicer deal.

What changes if analysis takes two seconds is the moment you run it. At 50 seconds it's a gate you hit once before pushing. At under two it can run on every save, and the type errors show up while you still remember why you wrote the line. That shift in habit is worth more than any benchmark chart, and it's the part Psalm's design could never fully give us, no matter how good its analysis is. I'd happily trade a few missing rules for that, at least on a feature branch.

So I'm curious where you stand, especially those of you maintaining libraries that ship precise docblock types. If two engines disagree about one of your @template signatures, which one do you consider wrong, and would you put your name behind a community-maintained spec for this type language, or do you think letting the tools converge on their own is the healthier path?