Some time ago, I decided to improve the codebase of one of my large actively developed Laravel projects to conform to strict larastan level 9 rule. At the first run, the analysis reported over 1200 errors, an absolutely massive undertaking for a single person.
I started over a year ago, slowly chipping away the errors alongside personal life, development of new features and other projects and it was quite painful at the start.
The project backend is heavily covered with tests I personally wrote. There
are over 600 tests and over 3400 assertions at the time of writing. This
fact gives me confidence in sudden changes in codebase, either via sed
(or its modern alternative sd
) or via linters like php-cs-fixer
.
The tests coverage also gives me a lot of confidence in using artificial
intelligence to do the changes in the codebase too, especially since I have
now found a way to step through hundreds of larastan errors in a day rather
than a month. Steps after running aider
with your favorite model:
- run analysis manually
- copy 3-7 errors from 1-3 files into chat
- paste the errors right into chat and press Enter a few times
- see errors resolved effortlessly
- write
/drop
to drop files from chat - rinse & repeat!
Most of the times it works just fine, since phpstan
's error response
contains file names, aider
just asks you to add them into the chat by
itself. As with all models, sometimes the solution that arises from it is a
complete nonsense and then a manual intervention is required, but I just
got rid of around 200 errors at around level 6 - 8 in two days this way and
felt great. All tests are still passing, I just made sure I reviewed all
the changes the model did, instead of coming up with them. Massive time
saver! Just keep an eye on the model context widow.
Down below is an output of such a process, for illustration, but not with
copy & paste. Instead an aider
command /run
is used to teach it how to
run analysis on a single file itself, because by default it does not
provide the --memory-limit
parameter or the correct
way to Laravel Sail (if you use it) and thus the analysis fails/crashes.
$ aider --model sonnet dev
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Aider v0.85.1
Main model: anthropic/claude-sonnet-4-20250514 with diff edit format, infinite output
Weak model: anthropic/claude-3-5-haiku-20241022
Git repo: .git with 456 files
Repo-map: using 4096 tokens, auto refresh
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
> /run ./vendor/bin/sail exec laravel.test vendor/bin/phpstan --memory-limit=24G analyse tests/Feature/ItemTest.php
Note: Using configuration file /var/www/html/phpstan.neon.
1/1 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%
------ ---------------------------------------------------------------------
Line ItemTest.php
------ ---------------------------------------------------------------------
272 Casting to int something that's already int.
🪪 cast.useless
💡 Because the type is coming from a PHPDoc, you can turn off this
check by setting treatPhpDocTypesAsCertain: false in your
phpstan.neon.
417 Casting to int something that's already int.
🪪 cast.useless
💡 Because the type is coming from a PHPDoc, you can turn off this
check by setting treatPhpDocTypesAsCertain: false in your
phpstan.neon.
------ ---------------------------------------------------------------------
[ERROR] Found 2 errors
Add 1.1k tokens of command output to the chat? (Y)es/(N)o [Yes]:
Added 22 lines of output to the chat.
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
> What's wrong? Fix
The PHPStan error shows there are useless int casts on lines 272 and 417 in tests/Feature/ItemTest.php. These lines are casting values that are already integers.
To fix this, I need you to add the file tests/Feature/ItemTest.php to the chat so I can see the specific lines and remove the unnecessary (int) casts.
Tokens: 11k sent, 76 received. Cost: $0.04 message, $0.04 session.
tests/Feature/ItemTest.php
Add file to the chat? (Y)es/(N)o/(D)on't ask again [Yes]:
Looking at the PHPStan errors, I need to remove the unnecessary (int) casts on lines 272 and 417. These are casting $item->internal_number which is already an integer according to the type system.
tests/Feature/ItemTest.php
<<<<<<< SEARCH
'internal_number' => (int) $item->internal_number,
=======
'internal_number' => $item->internal_number,
>>>>>>> REPLACE
tests/Feature/ItemTest.php
<<<<<<< SEARCH
'internal_number' => (int) $item->internal_number,
=======
'internal_number' => $item->internal_number,
>>>>>>> REPLACE
./vendor/bin/sail exec laravel.test vendor/bin/phpstan --memory-limit=24G analyse tests/Feature/ItemTest.php
Tokens: 12k sent, 171 received. Cost: $0.04 message, $0.07 session.
Applied edit to tests/Feature/ItemTest.php
Commit 7b8b075 refactor: remove unnecessary int casts in ItemTest.php
./vendor/bin/sail exec laravel.test vendor/bin/phpstan --memory-limit=24G analyse tests/Feature/ItemTest.php
Run shell command? (Y)es/(N)o/(D)on't ask again [Yes]:
Running ./vendor/bin/sail exec laravel.test vendor/bin/phpstan --memory-limit=24G analyse tests/Feature/ItemTest.php
Note: Using configuration file /var/www/html/phpstan.neon.
1/1 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%
[OK] No errors
Enjoy!