PHPStan level 8 in a legacy codebase: survival guide
We committed to getting a 5-year-old Laravel codebase to PHPStan level 8. Sharing lessons learned after three months.
Starting state: level 0 with 2400 errors at level 8. Current state: 180 errors remaining.
The --generate-baseline flag is essential for legacy codebases. It creates a file recording the current errors which are then treated as suppressed. You can raise the bar incrementally without blocking CI on the existing violations.
The biggest category of errors in our codebase was dynamic property access on stdClass results from DB queries. Adding typed result objects eliminated about 600 errors in one refactor.
ok we just tried this on our codebase and got 3800 errors at level 8 lmao. gues we have some work to do
Level 6 to 7 is the hardest jump because of the strict null checking. Every nullable return suddenly needs a null guard. Worth doing in stages: fix level 6 first, then address the nullables separately.
Larastan extensions help with Laravel-specific patterns (facades, magic methods, Eloquent models). Without the extension, PHPStan flags Laravel idioms as errors because it cannot see through the magic.
We added the PHPStan rule that checks that every thrown exception is either caught or declared. Found 40 exception paths we never handled. Most were ignorable but three were real bugs.
One trick: add /* @phpstan-ignore-next-line */ sparingly and with a TODO comment explaining why. It makes the suppression visible in code review instead of hiding it in the baseline.
```php blocks are runnable.