migrations
Database schema change files. Always create new files for changes — never modify existing migrations. Use descriptive names, proper foreign key constraints, and reversible `down()` methods.
Files1
View on GitHubSKILL.mdresources/boost/skills/migrations/SKILL.md
GitHubMigrations
When to Apply
- Apply for schema changes: tables, columns, indexes, foreign keys, constraints.
- Apply when rollout/rollback behavior must be explicit and versioned.
- Do not use for long-running data backfills or one-off data repair scripts.
- For zero-downtime rollout strategies, pair this skill with deployment/runbook guidance.
Preconditions
- Database connection and target environment are known.
- Migration file naming follows timestamp prefix ordering.
- Risky operations (
dropColumn, type changes, destructive renames) are reviewed with backup/rollback plan.
Process
1. Create a New Migration File
- Never edit previously committed migration files.
- Use descriptive names, for example
add_status_to_invoices_table. - Generate with Laravel command:
php artisan make:migration add_status_to_invoices_table --table=invoices
2. Implement up() with Focused Changes
- Keep one concern per migration.
- Add indexes for foreign keys and common filters.
- Prefer string-backed status columns for enum-like states.
3. Implement Safe down() Reversal
- Reverse every structural change in
up()where possible. - Document irreversible/data-loss risks in comments and PR notes.
- Remember: dropping a column restores schema only; removed data is not recovered.
4. Validate Migration Lifecycle
- Run
php artisan migrateandphp artisan migrate:rollback --step=1locally. - Confirm table timestamps behavior explicitly when removing timestamps:
$table->dropTimestamps();(dropscreated_atandupdated_attogether).
Examples
public function up(): void
{
Schema::create('invoices', function (Blueprint $table) {
$table->id();
$table->foreignId('order_id')->constrained()->cascadeOnDelete();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->string('status')->default('draft');
$table->decimal('amount', 10, 2);
$table->date('due_date');
$table->timestamp('paid_at')->nullable();
$table->timestamps();
$table->index('status');
});
}
public function down(): void
{
Schema::dropIfExists('invoices');
}
// Adding a column to an existing table
public function up(): void
{
Schema::table('invoices', function (Blueprint $table) {
$table->string('reference')->nullable()->after('status');
});
}
public function down(): void
{
Schema::table('invoices', function (Blueprint $table) {
$table->dropColumn('reference');
});
}
// Dropping timestamps explicitly (schema reversal only, not data recovery)
public function up(): void
{
Schema::table('invoices', function (Blueprint $table) {
$table->dropTimestamps();
});
}
public function down(): void
{
Schema::table('invoices', function (Blueprint $table) {
$table->timestamps();
});
}
Checklists
- Migration is new (no edits to existing historical migrations).
- File name is descriptive and timestamp ordered.
-
up()anddown()are implemented and tested locally. - Destructive operations include rollback and data-loss notes.
- Indexes/foreign keys are added for query and integrity needs.
Anti-Patterns
- Modifying existing migration files instead of creating new ones
- Omitting the
down()method or leaving it empty - Not adding indexes on foreign key columns and frequently queried columns
- Creating a single migration that makes multiple unrelated schema changes
- Using integer status defaults when domain status is a named state (
draft,paid,cancelled) - Assuming
down()can recover data that was dropped inup()
References
- Laravel Migrations
- Laravel Migration Events and Commands
resources/boost/skills/models/SKILL.md(schema-to-model alignment)resources/boost/skills/enums/SKILL.md(status value conventions)