nkmryu / laravel-strong-migrations
Detect potentially dangerous Laravel database migrations before they are deployed.
Package info
github.com/nkmryu/laravel-strong-migrations
pkg:composer/nkmryu/laravel-strong-migrations
Requires
- php: ^8.3
- illuminate/support: ^12.0|^13.0
- nikic/php-parser: ^5.0
Requires (Dev)
- laravel/pint: ^1.0
- orchestra/testbench: ^10.0|^11.0
- phpunit/phpunit: ^11.5|^12.0
README
Laravel Strong Migrations detects potentially dangerous database migrations before they are deployed. It brings the safety-oriented workflow of ankane/strong_migrations to Laravel projects using structural PHP AST analysis.
Requirements
- PHP 8.3 or later
- Laravel 12 or later
Install
Install the package with Composer:
composer require --dev nkmryu/laravel-strong-migrations
Publish the optional configuration file:
php artisan vendor:publish --tag=strong-migrations-config
Usage
Lint the configured migration paths:
php artisan migration:lint
Use CI mode to fail when a finding meets the configured fail_on severity:
php artisan migration:lint --ci
Use JSON output or override the paths when integrating with other tools:
php artisan migration:lint --json --path=database/migrations --path=packages/example/database/migrations
Add #[SafetyAssured(reason: '...')] to a migration's anonymous class only after reviewing and accepting the operation's safety.
Rules
| Rule | Severity | What it detects |
|---|---|---|
DropColumnRule |
High | Columns dropped while deployed application instances may still use them. |
DropTableRule |
High | Tables dropped while deployed application instances may still query them. |
RenameColumnRule |
High | Column renames that are incompatible with old application instances. |
RenameTableRule |
High | Table renames that are incompatible with old application instances. |
ChangeColumnRule |
High | Column changes that may lock or rebuild a table. |
AddNotNullColumnRule |
High | Required columns added without a default for existing rows. |
RawDdlRule |
High | Raw DDL that bypasses structural safety checks. |
AddIndexRule |
Medium | Index creation that may scan a table and block writes. |
AddUniqueIndexRule |
Medium | Unique index creation that may block writes or fail on duplicate data. |
Configuration
The published config/strong-migrations.php file supports these keys:
| Key | Description |
|---|---|
migration_paths |
Files and directories scanned by migration:lint. The default is database/migrations. |
contract_paths |
Migration paths excluded from linting because they contain deliberate contract operations. In an expand/contract workflow, place migrations that remove old columns or tables here and run them only after all deployed code has stopped using the old schema. The default is database/migrations/post-deploy. |
start_after |
An optional migration timestamp in YYYY_MM_DD_HHMMSS format. Migrations at or before this value are ignored, which lets an existing project adopt the package without reporting its full migration history. Newer migrations remain checked. |
disabled_rules |
Rule class names to skip, such as AddIndexRule. Keep this list empty unless the project has a documented alternative safety control. |
fail_on |
The minimum severity that makes migration:lint --ci fail. Use high to fail only on high-severity findings or medium to fail on both medium- and high-severity findings. |
Safety assurance
Use SafetyAssured only when a specific migration has been reviewed and the reason can be recorded in code. Apply the attribute to the anonymous migration class:
<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; use Nkmryu\StrongMigrations\SafetyAssured; return new #[SafetyAssured(reason: 'The application stopped reading this column in the previous release.')] class extends Migration { public function up(): void { Schema::table('users', function (Blueprint $table): void { $table->dropColumn('legacy_name'); }); } };
Why
Zero-downtime schema changes usually follow the expand/contract pattern: add the new schema first, migrate application behavior and data, then remove the old schema later. During a deployment, old application instances may still run while migrations and new instances start, so an immediate rename or removal can break live requests. Laravel Strong Migrations catches operations that violate this overlap and points toward staged alternatives. This package is inspired by and pays respect to ankane/strong_migrations, which established this safety-oriented workflow for Rails applications.