Skip to content

🔄 Migration Guide

Comprehensive guide for gradually adopting these coding standards in existing Laravel projects without disrupting development.

📋 Overview

Migrating an existing project to new coding standards is a journey, not a sprint. This guide provides a pragmatic, step-by-step approach to improve your codebase incrementally.

🎯 Migration Strategy

The Boy Scout Rule

"Always leave the code better than you found it."

Apply standards to code you touch rather than attempting massive refactoring.

📊 Phase 1: Assessment (Week 1)

1.1 Audit Current Codebase

bash
# Check PSR-12 compliance
./vendor/bin/phpcs --standard=PSR12 app/

# Run static analysis
./vendor/bin/phpstan analyse app/ --level=5

# Check for N+1 queries
# Use Laravel Telescope to monitor queries

# Generate code metrics
./vendor/bin/phpmetrics --report-html=metrics app/

1.2 Document Current State

Create a baseline document:

markdown
# Codebase Audit - [Date]

## Metrics
- Total Files: 450
- Lines of Code: 85,000
- PSR-12 Compliance: 35%
- Test Coverage: 42%
- Average Cyclomatic Complexity: 8.5

## Major Issues
- [ ] Fat controllers (45% of controllers > 200 lines)
- [ ] N+1 queries in 12 endpoints
- [ ] No type declarations in 60% of methods
- [ ] Missing input validation in 30% of endpoints
- [ ] Direct model access in 80% of controllers

1.3 Prioritize Improvements

PriorityIssueImpactEffort
CriticalN+1 QueriesHighMedium
CriticalMissing ValidationHighLow
HighType DeclarationsMediumLow
HighFat ControllersMediumHigh
MediumPSR-12 ComplianceLowLow

🔧 Phase 2: Foundation Setup (Week 2)

2.1 Install Code Quality Tools

bash
# Install PHP CS Fixer
composer require friendsofphp/php-cs-fixer --dev

# Install PHPStan
composer require phpstan/phpstan --dev

# Install Laravel Pint
composer require laravel/pint --dev

# Install Telescope (development)
composer require laravel/telescope --dev
php artisan telescope:install

2.2 Configure Tools

php
// .php-cs-fixer.php
<?php

$finder = PhpCsFixer\Finder::create()
    ->in(__DIR__)
    ->exclude(['bootstrap', 'storage', 'vendor'])
    ->name('*.php')
    ->notName('*.blade.php');

return (new PhpCsFixer\Config())
    ->setRules([
        '@PSR12' => true,
        'strict_param' => true,
        'array_syntax' => ['syntax' => 'short'],
        'ordered_imports' => ['sort_algorithm' => 'alpha'],
        'no_unused_imports' => true,
    ])
    ->setFinder($finder);
neon
# phpstan.neon
parameters:
    level: 5
    paths:
        - app
    excludePaths:
        - app/Console/Kernel.php
    checkMissingIterableValueType: false

2.3 Setup CI/CD Checks

yaml
# .github/workflows/code-quality.yml
name: Code Quality

on: [push, pull_request]

jobs:
  code-quality:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      
      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.2'
      
      - name: Install Dependencies
        run: composer install --prefer-dist --no-progress
      
      - name: Run PHP CS Fixer
        run: ./vendor/bin/php-cs-fixer fix --dry-run --diff
      
      - name: Run PHPStan
        run: ./vendor/bin/phpstan analyse
      
      - name: Run Tests
        run: php artisan test --coverage

🚀 Phase 3: Incremental Migration (Weeks 3-12)

3.1 Start with New Code

Rule: All new code must follow the standards completely.

php
<?php

// New controller - follows all standards
declare(strict_types=1);

namespace App\Http\Controllers\Api\V1;

use App\Http\Controllers\Controller;
use App\Http\Requests\StoreProductRequest;
use App\Http\Resources\ProductResource;
use App\Services\ProductService;
use Illuminate\Http\JsonResponse;

class ProductController extends Controller
{
    public function __construct(
        private ProductService $productService
    ) {}

    public function store(StoreProductRequest $request): JsonResponse
    {
        $product = $this->productService->createProduct($request->validated());
        
        return response()->json([
            'data' => new ProductResource($product),
            'message' => 'Product created successfully',
        ], 201);
    }
}

3.2 Refactor Critical Paths

Focus on frequently used and business-critical code first.

Example: Refactor Order Processing

Before: Fat Controller

php
<?php

// OLD CODE - Don't modify all at once
class OrderController extends Controller
{
    public function store(Request $request)
    {
        // 200+ lines of business logic
    }
}

Migration Steps:

Step 1: Extract validation

php
<?php

// Create Form Request
class StoreOrderRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'items' => ['required', 'array', 'min:1'],
            'items.*.product_id' => ['required', 'exists:products,id'],
            'items.*.quantity' => ['required', 'integer', 'min:1'],
        ];
    }
}

// Update controller
class OrderController extends Controller
{
    public function store(StoreOrderRequest $request) // Now using Form Request
    {
        // Business logic
    }
}

Step 2: Create service layer

php
<?php

// Create Service
class OrderService
{
    public function createOrder(User $user, array $data): Order
    {
        // Move business logic here
    }
}

// Update controller
class OrderController extends Controller
{
    public function __construct(
        private OrderService $orderService
    ) {}

    public function store(StoreOrderRequest $request)
    {
        $order = $this->orderService->createOrder(
            auth()->user(),
            $request->validated()
        );
        
        return response()->json($order, 201);
    }
}

Step 3: Create repository

php
<?php

// Create Repository
class OrderRepository implements OrderRepositoryInterface
{
    public function create(array $data): Order
    {
        return Order::create($data);
    }
}

// Update Service
class OrderService
{
    public function __construct(
        private OrderRepositoryInterface $orderRepository
    ) {}

    public function createOrder(User $user, array $data): Order
    {
        return $this->orderRepository->create([
            'user_id' => $user->id,
            // ... other data
        ]);
    }
}

3.3 Fix N+1 Queries

php
<?php

// BEFORE: Identify N+1 with Telescope
$users = User::all();
foreach ($users as $user) {
    echo $user->posts->count(); // N+1!
}

// AFTER: Fix immediately
$users = User::withCount('posts')->get();
foreach ($users as $user) {
    echo $user->posts_count;
}

3.4 Add Type Declarations

php
<?php

// Use IDE or tools to add types incrementally

// BEFORE
public function getUser($id) {
    return User::find($id);
}

// AFTER
public function getUser(int $id): ?User {
    return User::find($id);
}

📝 Phase 4: Documentation & Training (Ongoing)

4.1 Team Training

markdown
# Training Schedule

## Week 1: Fundamentals
- PSR-12 and PHP Standards
- Type Declarations
- Naming Conventions

## Week 2: Architecture
- SOLID Principles
- Repository Pattern
- Service Layer

## Week 3: Practical
- Form Requests
- API Resources
- Testing Strategies

## Week 4: Advanced
- Performance Optimization
- Security Best Practices
- Code Review Process

4.2 Code Review Guidelines

Update your PR template:

markdown
## Code Quality Checklist

- [ ] Follows PSR-12 standards
- [ ] All methods have type declarations
- [ ] Uses Form Requests for validation
- [ ] Controllers are thin (< 50 lines)
- [ ] Business logic in Services
- [ ] Data access via Repositories
- [ ] No N+1 queries
- [ ] Tests included
- [ ] Documentation updated

## Migration Notes

If this touches legacy code:
- [ ] Applied standards to touched code
- [ ] Added tests for modified functionality
- [ ] Documented any technical debt left behind

🎯 Phase 5: Consolidation (Weeks 13+)

5.1 Establish Coding Dojo

Regular sessions to refactor legacy code together:

markdown
# Weekly Coding Dojo

## Format
- 2 hours every Friday afternoon
- Team selects one legacy file
- Refactor together following standards
- Discuss tradeoffs and decisions

## Benefits
- Team learning
- Shared ownership
- Consistent patterns
- Knowledge transfer

5.2 Track Progress

bash
# Monthly metrics
./vendor/bin/phpstan analyse --level=6  # Increase level gradually
./vendor/bin/phpmetrics --report-html=metrics app/

# Compare with baseline
echo "PSR-12 Compliance: Month 1: 35%, Month 3: 65%, Month 6: 85%"
echo "Test Coverage: Month 1: 42%, Month 3: 58%, Month 6: 75%"

⚠️ Migration Gotchas

Don't Do This

Big Bang Refactoring

bash
# DON'T: Refactor everything at once
# This breaks everything and blocks development
git checkout -b refactor-everything
# ... refactor 100 files ...
# Merge conflicts nightmare!

Mixing Features and Refactoring

# DON'T: Mix new features with refactoring in one PR
- Added user authentication (new feature)
- Refactored 50 controllers (refactoring)
- Added payment gateway (new feature)
- Restructured entire app (refactoring)

Ignoring Tests

php
// DON'T: Refactor without tests
// How do you know it still works?

Do This Instead

Incremental Improvements

PR #1: Extract validation to Form Request for OrderController
PR #2: Add repository for Order operations
PR #3: Create OrderService with business logic
PR #4: Add tests for Order creation flow

Refactor Then Feature

PR #1: Refactor OrderController (preparation)
PR #2: Add new discount feature (new feature on clean base)

Test First

php
// Add tests for existing behavior
// Then refactor with confidence
// Tests prove nothing broke

📊 Success Metrics

Track these metrics monthly:

MetricBaselineMonth 3Month 6Target
PSR-12 Compliance35%60%85%95%
Test Coverage42%60%75%80%
Avg Cyclomatic Complexity8.57.05.5< 5
Controllers > 100 lines45%30%15%< 10%
N+1 Queries12510

🎓 Learning Resources

For the Team

  1. Documentation - This standards guide
  2. Code Examples - Reference implementations
  3. Pair Programming - Learn by doing
  4. Code Reviews - Feedback and learning
  5. Coding Dojos - Group refactoring sessions

External Resources

✅ Migration Checklist

Preparation

  • [ ] Audit codebase and document current state
  • [ ] Install code quality tools
  • [ ] Configure CI/CD checks
  • [ ] Create migration plan with timeline

Execution

  • [ ] Apply standards to all new code
  • [ ] Fix critical issues (N+1, security)
  • [ ] Refactor high-traffic endpoints
  • [ ] Add tests incrementally
  • [ ] Update documentation

Consolidation

  • [ ] Establish code review process
  • [ ] Schedule regular refactoring sessions
  • [ ] Track metrics monthly
  • [ ] Celebrate improvements
  • [ ] Share learnings

🔄 Remember: Migration is a marathon, not a sprint. Focus on sustainable progress and team learning rather than perfection overnight.

Built with VitePress