Naar de inhoud
Recognized by Laravel Match je project Contact

Effectieve migrations schrijven

Voor het laatst bijgewerkt op:

Introductie

Migrations vormen de versiebeheer voor je databaseschema. Goed geschreven migrations zijn gericht, omkeerbaar en bevatten vanaf het begin de juiste indexering. Omdat migrations bevroren momentopnames zijn, vragen ze om speciale discipline, zodra ze naar productie zijn uitgerold, mogen ze nooit meer worden gewijzigd.

Waarom

  • Consistentie: Het gebruik van constrained() voor foreign keys zorgt voor automatische naamgeving en referentiële integriteit
  • Veiligheid: Uitgerolde migrations nooit wijzigen voorkomt inconsistente databasetoestanden tussen omgevingen
  • Performance: Indexes toevoegen in de migration in plaats van achteraf voorkomt vergeten performance-optimalisaties
  • Omkeerbaarheid: Het schrijven van down()-methodes maakt veilige rollbacks mogelijk tijdens mislukte deployments en in CI-pipelines
  • Duidelijkheid: Eén verantwoordelijkheid per migration maakt het eenvoudig om te herkennen wat er wanneer is veranderd

Geschikt voor

  • Alle Laravel-applicaties die migrations gebruiken
  • Teams met meerdere developers die aan hetzelfde databaseschema werken
  • Projecten met CI/CD-pipelines die migrations uitvoeren

Minder geschikt voor

  • N.v.t., deze praktijken gelden voor elk project dat Laravel-migrations gebruikt

Voorbeelden

Gebruik constrained() voor foreign keys

$table->foreignId('user_id')->constrained()->cascadeOnDelete();

// Non-standard names
$table->foreignId('author_id')->constrained('users');

Wijzig uitgerolde migrations nooit

// Bad: editing a migration that already ran in production
// 2024_01_01_create_posts_table.php
$table->string('slug')->unique(); // added after deployment

// Good: new migration to alter the table
// 2024_03_15_add_slug_to_posts_table.php
Schema::table('posts', function (Blueprint $table) {
    $table->string('slug')->unique()->after('title');
});

Voeg indexes toe in de migration

// Bad: no indexes on frequently queried columns
Schema::create('orders', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')->constrained();
    $table->string('status');
    $table->timestamps();
});

// Good: indexes added from the start
Schema::create('orders', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')->constrained()->index();
    $table->string('status')->index();
    $table->timestamp('shipped_at')->nullable()->index();
    $table->timestamps();
});

Spiegel column-defaults in model $attributes

Wanneer een column een database-default heeft, spiegel deze dan in het model zodat nieuwe instances de juiste waarden hebben vóór het opslaan:

// Migration
$table->string('status')->default('pending');

// Model
protected $attributes = [
    'status' => 'pending',
];

Schrijf omkeerbare down()-methodes

public function down(): void
{
    Schema::table('posts', function (Blueprint $table) {
        $table->dropColumn('slug');
    });
}

Voor bewust onomkeerbare migrations laat je een duidelijke comment achter en vereis je in plaats daarvan een corrigerende voorwaartse migration.

Houd migrations gericht

Meng nooit DDL (schemawijzigingen) en DML (datamanipulatie) in één migration:

// Bad: partial failure creates unrecoverable state
public function up(): void
{
    Schema::create('settings', function (Blueprint $table) { /* ... */ });
    DB::table('settings')->insert(['key' => 'version', 'value' => '1.0']);
}

// Good: separate migrations
// Migration 1: create_settings_table
Schema::create('settings', function (Blueprint $table) { /* ... */ });

// Migration 2: seed_default_settings
DB::table('settings')->insert(['key' => 'version', 'value' => '1.0']);

Meer info

Skill

Laravel Boost Skill

Gebruik deze skill om de richtlijn rechtstreeks toe te passen met een AI-assistent.

Use this skill when a Laravel task touches this best practice. It is self-contained so it can be installed independently by Laravel Boost or another agent-skill system.

## Core Guidance

Migrations are the version control for your database schema. Well-written migrations are focused, reversible, and include proper indexing from the start. Since migrations are frozen snapshots in time, they require special discipline, once deployed to production, they should never be modified.

## Why It Matters

- **Consistency**: Using `constrained()` for foreign keys ensures automatic naming and referential integrity
- **Safety**: Never modifying deployed migrations prevents inconsistent database states across environments
- **Performance**: Adding indexes in the migration rather than as an afterthought avoids forgotten performance optimizations
- **Reversibility**: Writing `down()` methods allows safe rollbacks during failed deployments and in CI pipelines
- **Clarity**: Keeping one concern per migration makes it easy to identify what changed and when

## Apply When

- All Laravel applications using migrations
- Teams with multiple developers working on the same database schema
- Projects with CI/CD pipelines that run migrations

## Be Careful When

- N/A, these practices apply to any project using Laravel migrations

## Canonical Source

- Full best practice: https://github.com/Dutch-Laravel-Foundation/best-practices/blob/main/database-and-eloquent-orm/write-effective-migrations/BEST_PRACTICE.md
- Dutch translation: https://github.com/Dutch-Laravel-Foundation/best-practices/blob/main/database-and-eloquent-orm/write-effective-migrations/translations/nl.md

## Workflow

1. Inspect the user's Laravel code before recommending changes.
2. Identify the narrow rule from this best practice that applies to the task.
3. Prefer Laravel's built-in conventions and documented APIs over custom abstractions.
4. Keep examples focused on this practice; reference other skills or practices when the task crosses boundaries.
5. Verify code changes with the project's available tests, linters, static analysis, or framework checks.

## Review Checklist

- The recommendation is Laravel-specific and grounded in this practice.
- Code examples use realistic Laravel file names, class names, and method names.
- The advice avoids mixing unrelated architecture, deployment, security, or testing topics.
- Related practices are mentioned when useful, but not re-explained in full.
- Dutch output, when requested, keeps framework and API names intact.