From Psalm to Pzoom

June 24, 2026
by Matt Brown (certified human)

TL;DR I used LLMs to port a PHP static analysis tool to Rust. The result, called Pzoom, is 10x faster — but speed isn’t everything.

Over a decade ago I started work on Psalm, a static analysis tool for PHP written in PHP. Over the intervening years Psalm added a best-in-class type inference engine and static security analysis, and helped usher in an era of typechecking for PHP that continues to this day.

Five years ago I joined Slack, which uses a fork of PHP called Hack. Hack is designed for better runtime performance than PHP, and natively supports many of the same type constructs that Psalm and similar tools support in code comment blocks.

Soon after joining Slack I began porting chunks of Psalm's PHP analysis code to Rust, targeting Hack instead of PHP. After six months of work, that project — Hakana — was ready.

In the years since, the idea of a direct Rust port of Psalm has been bouncing around the vacuum of my skull, but I never got around to laying hands on the keyboard. My two main worries were the lengthy time commitment, and the belief that nobody would actually want to use it.

Then Mago — a PHP static analysis tool written in Rust — was announced. I tried Mago and found it much faster than Psalm, but not quite as accurate. Around the same time we started using LLMs at work, and I began to wonder whether those LLMs could automate the bulk of a Rust rewrite of Psalm.

Building Pzoom

This year I experimented with a few different LLMs in my spare time to see if they could feasibly tackle a port.

In January I instructed a model to create a new project based on Psalm and Hakana, using Mago’s Rust-based PHP parser. After a few hours of work it created a semi-decent first pass, but most tests were failing. January’s LLMs weren’t good at fixing the bugs. I left the project alone for a couple of weeks.

Then I had another go with a February LLM, but it also faltered, topping out around 75% of tests passing. Instead of performing a faithful Rust port of source PHP code, the February model wrote code that approximated the rules it was supposed to port with test-specific heuristics, and then struggled to reason its way out of the many overlapping holes it had dug. Fixing one discrepancy invariably broke tests that had come to rely on it, so the model continually reverted good fixes just to get back to green.

A few months passed. I threw the problem at some of April and May’s LLMs (Opus 4.7, 4.8) but didn’t see much of an improvement — even though those LLMs have served me well for most other coding tasks.

Then came June’s LLM — Claude Fable 5. Fable was far better than Opus-grade models on this problem. Instead of having Fable start from scratch, I gave it the fairly broken output of the earlier attempts and its sharper diagnosis let it reason its way out of the holes. It needed only occasional redirection during some marathon autonomous coding sessions, and when I looked at the code it was pretty faithful to the two original sources.

All told, prompting, reviewing, and reprompting took about 100 hours of my time and ~$2,000 in tokens over the last 6 months. That's a lot of tokens, but cranking out 100,000 lines of Rust by hand would have taken me a couple of months of all-week grinding, and would have taken others longer.

Here's the Pzoom playground and source code.

Predictably, given it’s written in Rust, Pzoom is 10x faster than Psalm. It passes 99.9% of Psalm’s tests, producing near-identical output when analysing Psalm’s own PHP source code.

Not so fast

Making a static analysis tool 10x speedier might seem like a big win — for example, at Slack we’ve shaved minutes off CI time with the rewritten-in-Go TypeScript checker — but the picture is murkier in PHP.

Today most large new PHP projects use frameworks that rely on metaprogramming for the heavy lifting. That PHP "magic" resolves at runtime (which a static pass can't observe), so developers frequently resort to custom plugins, written in PHP, to help PHP typecheckers understand what’s going on.

When I first created Psalm I thought this was the wrong approach, preferring typecheckers like TypeScript and Sorbet (for Ruby) that are pretty opinionated. I quickly came to understand that many PHP developers preferred flexibility (and, oftentimes, greater leniency). This situation isn’t unique to PHP — the Python typechecker MyPy allows for custom plugins that can alter its understanding of Python code — but it does make it much harder for compiled tools like Mago (and now Pzoom) to get adopted widely.

I‘ve added a lightweight plugin system to Pzoom, but since there’s no scan-time execution of PHP scripts allowed there are limits to what it can do automatically.

Can LLMs rewrite everything in Rust now?

I don’t think we’ve reached the era of LLMs one-shotting complex ports like this.

With Pzoom it helped enormously that I'd written much of the code myself, and I could point out problems with the LLM's implementation as it progressed. Psalm also has 5,000 tests and its own large PHP codebase to exercise the end-to-end flow. Even though earlier LLMs overfitted to test passing, those tests — and the 1,500 additional ones that LLMs added — were a necessary check on the effort.

This experiment with the latest LLMs suggests that deep expertise is no longer necessary to get 90% of the way there — but it is still necessary to ship.

Software development isn’t just writing code

For the first few years I worked on Psalm, I stood by the mantra “if you build it they will come”. I was wrong! I then spent the next few years doing whatever I could to convince people that typecheckers could help them write better code.

I won't spend the same amount of time persuading people to use Pzoom. Unless developers really need the speedup and they're comfortable maintaining it themselves, they shouldn't take it on as a dependency. I’m mostly open-sourcing it because I think it’s neat.

In the age of LLMs, software is cheaper to produce, but a person who cares about the software they write and continues to maintain it is as valuable as ever.

Psalm is still very much cared for — currently maintained by Daniil Gentili — and this port surfaced a few bugs I've since fixed in Psalm itself. So even if nobody uses Pzoom, it's still produced some benefit.