What Is Difflock?
Difflock is a Laravel package by Rati Rukhadze that analyses pending migrations against the live database they will change. Rather than executing migrations against an empty test database, it reads migration source statically and combines that with the real schema and table-size metadata to surface problems before deployment.
Linting Pending Migrations
Run difflock:lint to analyse every pending migration:
php artisan difflock:lint
php artisan difflock:lint -v
php artisan difflock:lint --rule=drop-column
Consider this migration that looks harmless in a pull request:
Schema::table('orders', function (Blueprint $table) {
$table->string('channel');
$table->foreignId('customer_id')->constrained()->cascadeOnDelete();
$table->string('card_number', 32)->nullable();
$table->index('status');
});
Schema::table('customers', function (Blueprint $table) {
$table->dropColumn('legacy_token');
$table->renameColumn('name', 'full_name');
});
On a populated database, Difflock flags the non-null channel column (no default, will fail on existing rows), the destructive dropColumn(), the rename, and the fact that cascadeOnDelete() removes child rows inside the database engine — bypassing model events, observers, and soft deletes.
Additional rules cover change-column (compares a ->change() call with the live column definition), unindexed-foreign-key, and redundant-index (e.g., adding an index on (status) when (status, created_at) already exists).
When no pending migrations exist, the command audits all migration files instead of producing an empty report. Existing projects can accept their current backlog with:
php artisan difflock:lint --all --accept
This writes database/difflock/accepted.json as a baseline for future findings.
Recording and Diffing Schema Baselines
Difflock records an observed schema rather than rebuilding an expected one from migration history, which avoids interpreting migrations that contain conditionals, loops, or raw SQL.
php artisan difflock:diff --save
This writes database/difflock/schema.json — commit it to version control. Later runs compare the current connection against that snapshot. You can also compare two configured connections directly:
php artisan difflock:diff --from=staging --to=production
The baseline captures tables, columns, indexes, defaults, and foreign keys. It never stores row data or credentials.
CI Integration and Migration Guard
Installing Difflock does not alter php artisan migrate. The guard only activates when you use its own command:
php artisan difflock:migrate
When findings reach the configured block level, it stops before Laravel writes to the database. Use --allow-risky to deliberately bypass the guard; Laravel's --force flag remains the separate production confirmation.
For CI pipelines, a single command covers both drift detection and migration linting:
- run: php artisan difflock:check --ci
Exit codes: 0 = pass, 1 = drift or findings at threshold, 2 = check cannot run.
MCP Support for AI Coding Agents
php artisan difflock:mcp starts a standalone MCP server over stdio. Its four tools provide table context, migration linting, schema-drift checks, and rule documentation. An AI coding agent can check a migration before writing the file, using the live schema and table statistics. The difflock:explain command generates a Markdown briefing for a migration without calling any external API.
Installation
Difflock 1.0.0 requires PHP 8.3 and supports Laravel 12 and 13. It works with MySQL, MariaDB, PostgreSQL, and SQLite.
composer require heyosseus/difflock --dev
php artisan vendor:publish --tag=difflock-config
php artisan difflock:doctor
difflock:doctor reports the connection, available tables, pending migrations, registered rules, and whether the configured database role can write.
Key Takeaways
- Lints migrations against the live schema, catching issues an empty test database misses
- Detects destructive operations:
dropColumn, non-null columns on populated tables, cascade deletes - Records JSON schema baselines you can commit and diff across environments or connections
- Guards
php artisan migratewith configurable block levels and CI-friendly exit codes - Exposes MCP tools so AI coding agents can check migrations before writing them to disk
- Requires PHP 8.3, supports Laravel 12 and 13, MySQL, MariaDB, PostgreSQL, and SQLite
Source: Difflock: Lint Laravel Migrations and Diff Your Schema — Laravel News