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:

  1. run analysis manually
  2. copy 3-7 errors from 1-3 files into chat
  3. paste the errors right into chat and press Enter a few times
  4. see errors resolved effortlessly
  5. write /drop to drop files from chat
  6. 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!