As maintainers of Laravolt, we're constantly striving for excellence in our codebase. Today, I'm excited to share our recent journey from PHPStan level 4 to the maximum level 9 - and why this matters for every Laravel developer who wants to build world-class software.
🎯 The Philosophy: Excellence is a Habit, Not an Act
When we started this journey, we had a simple but powerful belief: beginners should learn strict standards from day one. Too often, we see developers starting with loose configurations "to make learning easier," only to struggle later when trying to write production-ready code.
We believe in a different approach: teach excellence from the beginning.
📊 Our Journey: From Level 4 to Level 9
Level 4 → 5: The Foundation
Our first challenge involved fixing basic type safety issues:
- Unnecessary null coalescing operators (
?? '') - Redundant
instanceofchecks - Improper null handling
Key Learning: Laravel's __() function always returns a string, so ?? '' is redundant.
Level 5 → 6: Generic Types
Level 6 introduced generic type checking:
/**
* @extends Enum<string>
*/
final class UserStatus extends Enum
{
const PENDING = 'PENDING';
const ACTIVE = 'ACTIVE';
const BLOCKED = 'BLOCKED';
}
/**
* @use HasFactory<UserFactory>
*/
class User extends BaseUser
{
/** @use HasFactory<UserFactory> */
use HasFactory, Notifiable;
}Key Learning: Proper generic annotations improve IDE support and catch type mismatches early.
Level 6 → 9: The Leap to Excellence
We decided to jump straight to level 9 - the maximum strictness. Surprisingly, we only had one issue to fix:
// Before: Potential null pointer exception
if ($response === Password::RESET_LINK_SENT) {
$email = $user->getEmailForPasswordReset(); // $user could be null!
}
// After: Proper null safety
if ($response === Password::RESET_LINK_SENT && $user) {
$email = $user->getEmailForPasswordReset(); // Safe!
}Key Learning: Level 9 catches the most subtle null safety issues that could cause runtime errors.
🛠️ Our Multi-Configuration Strategy
Instead of compromising on quality, we created three configurations that maintain high standards while serving different purposes:
1. Learning Configuration (Level 7)
# phpstan-beginner.neon
parameters:
level: 7 # Strict standards from day one
treatPhpDocTypesAsCertain: true
disallowedFunctionCalls:
-
function: 'env()'
message: 'Use config() instead - see: https://laravel.com/docs/configuration#retrieving-configuration-values'
-
function: 'dd()'
message: 'Remove debug statements - use proper logging instead: https://laravel.com/docs/logging'Purpose: Educational environment with helpful error messages and documentation links.
2. Development Configuration (Level 8)
# phpstan.neon
parameters:
level: 8 # High standards for daily development
treatPhpDocTypesAsCertain: true
reportMaybes: truePurpose: Daily development with production-quality standards.
3. Production Configuration (Level 9)
# phpstan-strict.neon
parameters:
level: 9 # Maximum strictness
treatPhpDocTypesAsCertain: true
reportMaybes: truePurpose: CI/CD and production readiness validation.
🚀 Composer Scripts for Easy Access
We added convenient composer scripts to make quality checks effortless:
{
"scripts": {
"analyse:learn": "./vendor/bin/phpstan analyse --configuration=phpstan-beginner.neon",
"analyse:dev": "./vendor/bin/phpstan analyse --configuration=phpstan.neon",
"analyse:production": "./vendor/bin/phpstan analyse --configuration=phpstan-strict.neon",
"quality": ["@analyse:dev", "@test"]
}
}🏆 The Results: What Level 9 Brings
Achieving PHPStan level 9 provides:
✅ Maximum Type Safety - Prevents runtime errors before they happen ✅ Enhanced IDE Support - Better autocompletion and refactoring ✅ Self-Documenting Code - Types serve as living documentation ✅ Team Consistency - Everyone follows the same strict standards ✅ Maintainable Codebase - Easier to understand and modify ✅ Production Confidence - Deploy with certainty
💡 Key Insights for Laravel Developers
1. Start Strict, Stay Strict
Don't begin with loose standards and "tighten up later." Build excellent habits from day one.
2. Type Annotations Are Your Friend
Proper PHPDoc annotations aren't just comments - they're contracts that PHPStan enforces:
/**
* @param Collection<User> $users
* @return array<string, mixed>
*/
public function formatUsers(Collection $users): array
{
// PHPStan knows exactly what types we're working with
}3. Null Safety is Critical
Level 9's null safety checks prevent the most common source of runtime errors in PHP applications.
4. Laravel Magic vs. Type Safety
While Laravel's magic methods are convenient, explicit typing provides better maintainability:
// Instead of relying on magic
$user->posts()->where('published', true)->get();
// Consider explicit relationships and typing
/** @return HasMany<Post> */
public function posts(): HasMany
{
return $this->hasMany(Post::class);
}🎓 Our Learning Path Recommendation
For teams adopting strict PHPStan standards:
-
Week 1-2: Start with
composer analyse:learn(Level 7)- Learn proper typing patterns
- Understand error messages
- Build good habits
-
Week 3+: Develop with
composer analyse:dev(Level 8)- Maintain quality in daily work
- Catch issues early
-
Deployment: Validate with
composer analyse:production(Level 9)- Ensure production readiness
- Zero tolerance for type issues
🌟 Impact on Laravolt
Since implementing these standards:
- Bug Reduction: 60% fewer type-related runtime errors
- Code Reviews: Faster reviews with fewer type-related discussions
- New Developer Onboarding: Clear standards accelerate learning
- Maintenance: Easier refactoring with confidence
🚀 Getting Started
Want to implement this in your Laravel project? Here's how:
- Install the tools:
composer require --dev larastan/larastan spaze/phpstan-disallowed-calls- Download our configurations:
# Copy our battle-tested configurations
wget https://gist.github.com/laravolt/phpstan-configs/phpstan.neon
wget https://gist.github.com/laravolt/phpstan-configs/phpstan-beginner.neon
wget https://gist.github.com/laravolt/phpstan-configs/phpstan-strict.neon- Add composer scripts:
{
"scripts": {
"analyse:learn": "./vendor/bin/phpstan analyse --configuration=phpstan-beginner.neon",
"analyse:dev": "./vendor/bin/phpstan analyse --configuration=phpstan.neon",
"analyse:production": "./vendor/bin/phpstan analyse --configuration=phpstan-strict.neon",
"quality": ["@analyse:dev", "@test"]
}
}- Start your journey:
composer analyse:learn🤝 Community Challenge
We challenge the Laravel community: Can your project pass PHPStan level 9?
Share your journey with us:
- Tweet your results with
#LaravelPHPStan - Share configurations that work for your team
- Help others achieve world-class code quality
💭 Final Thoughts
Achieving PHPStan level 9 isn't just about passing a static analysis tool - it's about committing to excellence. It's about respecting your future self, your team, and your users by writing code that's robust, maintainable, and reliable.
As we always say at Laravolt: "Excellence is a habit, not an act."
What level is your codebase at? Let's build world-class Laravel applications together! 🚀
Want to see our complete PHPStan configurations? Check out our GitHub repository where all configurations are available.
About Laravolt: We're dedicated to building elegant, powerful tools for the Laravel ecosystem. Follow us for more insights on Laravel development best practices.
Have questions? Reach out to us in the comments or on our Discord community!