doctrine/migrations
Doctrine Migrations is a database schema migration library for PHP, letting you version and track schema changes with a robust CLI and API. It integrates with Doctrine DBAL and ORM to safely apply, roll back, and manage migrations across your projects.
Release delta (3.9.8). A two-PR patch release with no new features and no breaking changes. The substantive item is PR #1597, which fixes a doctrine/dbal 4.5 deprecation surfaced from within the migrations engine; the second PR is a Dependabot CI bump. Net effect on the assessment: it materially strengthens the DBAL-coupling risk item below, adds one small deprecation-hygiene consideration for our CI, and leaves every other finding unchanged.
Architecture fit. doctrine/migrations is a standalone, framework-agnostic migration engine built on doctrine/dbal and symfony/console. It is not a Laravel package in any meaningful sense: Laravel already ships Illuminate\Database\Migrations with its own migrations table, its own artisan command surface, and its own repository model. Adopting this package does not extend Laravel's migration system — it stands up a second, parallel one. That is the central architectural fact of this evaluation, and every downstream risk flows from it.
The package's genuine differentiator is diff generation: migrations:diff compares Doctrine ORM mapping metadata against live schema introspection and emits versioned migration classes. That value only materialises if we are using Doctrine ORM (via laravel-doctrine/orm or equivalent). If the application is Eloquent-first, we would be adopting a heavier engine, a second version table, and a Symfony Console dependency chain to hand-write the same SQL we already hand-write — a net negative.
Integration feasibility. Technically feasible, but only under a scoped ownership model. The package is consumed inside a Laravel app by registering a Configuration object (migrations paths, table_storage, all_or_nothing, transactional, schema_filter) via a service provider, then either invoking the library's command classes directly or registering them as artisan commands. This is well-trodden ground through community bridges; it is also entirely bespoke — no first-party support, no Laravel documentation, no upgrade contract.
Technical risk. Ranked:
migrations:diff proposes dropping tables that exist in the database but are absent from Doctrine ORM metadata. In a mixed Eloquent/Doctrine app, an unconfigured schema_filter will generate destructive migrations against Eloquent-owned tables. This is the single most likely production incident and must be mitigated by configuration and CI gating, not review discipline.all_or_nothing and transactional provide far weaker guarantees than the configuration names imply — a mid-migration failure can leave partial schema applied with no version row recorded, requiring manual reconciliation via migrations:version.doctrine/dbal major-version coupling (medium — revised downward, directionally). Laravel ≤10 pins DBAL v3 for column modification support; Laravel 11+ removed that requirement, freeing DBAL v4. Any constraint conflict forces us onto an older doctrine/migrations line. 3.9.8 is the first release we can point at as evidence that the 3.9 maintenance line is actively tracking DBAL 4.5, so the combination "Laravel 11+, DBAL 4.5, migrations 3.9.x" is now a supported and tested path rather than an assumption. This does not remove the constraint math from the spike — it removes the suspicion that the 3.9 line is frozen against DBAL v4. If we are on DBAL 4.5, treat 3.9.8 as the version floor.3.9.8 or later and surface deprecations in CI rather than in production.Key questions.
>=3.9.8 floor?down() in production?dependents: 0 figure a data artefact? For a package that underpins DoctrineMigrationsBundle, it almost certainly is; it should be excluded from the decision.Stack fit. Adopt only if Doctrine ORM is genuinely part of the target architecture. The defensible pattern is bounded-context ownership: Doctrine owns schema for a specific domain (e.g., a new service module introduced with laravel-doctrine), Eloquent owns the legacy core, and the two never touch the same tables. This gives a clean configuration boundary — a dedicated migrations_paths namespace, a distinct table_storage.table_name, and a schema_filter regex that scopes diff generation to Doctrine-owned tables only.
The anti-pattern to reject outright is a whole-database switchover of an existing Eloquent application. The migration cost is a full rewrite of every historical migration into DBAL-compatible form, with no functional gain on tables that Eloquent already manages adequately.
Migration path.
doctrine/migrations to ^3.9.8 as the floor. Prove migrations:diff against a throwaway schema with schema_filter configured, and verify that Eloquent-owned tables are excluded.Compatibility. Verify against the actual Laravel and PHP versions in the estate, not the package's declared matrix. Symfony Console version alignment matters: recent releases added Symfony 8 component support, so a modern Laravel should align cleanly, but this needs confirmation in the spike rather than assumption. The doctrine/dbal resolution is the most likely blocker; 3.9.8 improves the odds on DBAL v4 but does not change the fact that this is a constraint-solving exercise performed at spike time. Add a deprecation-reporting step to CI so library-internal deprecations against our DBAL version are caught on upgrade, not in the deploy window.
Sequencing. Config and CI gating must land before the first real migration, not after. Concretely: migrations:diff --dry-run in CI to fail on ORM/schema drift; migrations:up-to-date (or status) as a deploy gate; migrations:migrate --dry-run output reviewed for destructive statements. Add the external lock before the second app instance exists. Because 3.9.x is patching dependency compatibility rather than shipping features, schedule a deliberate patch-review cadence (monthly is sufficient) rather than auto-merging: these updates change dependency floors, and a merged patch that raises the DBAL floor can invalidate a working deploy.
Maintenance. Doctrine is foundation-backed with a small, stable core of maintainers (the release history is dominated by a handful of names) and a disciplined patch cadence. The 3.9.8 delta is instructive about what that cadence is now delivering: dependency-compatibility fixes from the community, plus Dependabot housekeeping. That is a healthy quality signal and a thin bus-factor signal — two contributors, one of them a bot. Observe also that 3.9.x is now in compatibility-only mode, which caps the surprise surface (no new features to regress) while guaranteeing ongoing floor movement in doctrine/dbal. We inherit the DBAL upgrade treadmill and the 4.0 transition as unplanned work; budget for it explicitly rather than treating it as incidental.
Support. MIT license, permissive and unproblematic. Documentation lives on doctrine-project.org, not in the repo — the README is a pointer only. There is no Laravel-specific guidance anywhere, so internal documentation is a deliverable, not a nice-to-have. Support for our integration layer is entirely ours. Note that a bugfix release at this granularity (one engine fix, one CI bump) is not a support event for us — but it is a reminder that our support model must include "read the patch notes and decide whether to bump," which needs a named owner.
Scaling. Three constraints bite:
gh-ost/pt-online-schema-change as a separate, hand-managed process, which partially defeats the tool's value on the tables that matter most.migrations_paths. If multi-tenancy is on the roadmap, validate this before committing.Failure modes to document and rehearse.
migrations:version.sync-metadata-storage.schema_filter misconfiguration — destructive diff against Eloquent tables (the headline risk).down() execution in production.>=3.9.8 pin).Ramp-up. The team's mental model is artisan: php artisan migrate, migrate:rollback, a single migrations table. This package asks them to learn Symfony Console invocation, namespaced migration paths, a second version table, and a diff workflow with a filtering language. The conceptual distance is modest but non-zero, and the failure modes are asymmetric — a mistake here is destructive schema change, not a bug. Plan one sprint of spike plus written runbook, and require a second engineer to review any generated migration before it executes. Do not let migrations:diff output ship unreviewed.
How can I help you explore Laravel packages today?