fervo/release-phase-migrations-bundle
A Symfony bundle for Doctrine migrations that adds release phases, letting you group schema and data changes and run them in a controlled order during deployment. Tag migrations by phase, run the right set per release, and keep deployments predictable across environments.
Architecture fit — poor as an artifact, sound as a pattern. This is a Symfony bundle, not a Laravel package. It ships a Bundle subclass, DI extension, and YAML configuration — none of which have any meaning in Laravel's service-provider model. Zero code is directly reusable; what transfers is the idea, which is roughly 150–250 lines of logic.
The idea is: acquire a database-native advisory lock, run migrations, release. The naming points to release-phase execution (e.g., Heroku release dynos, or a Kubernetes Job/init container), where N deploy replicas may attempt migrations simultaneously against one database. That concurrency problem is real for Laravel shops deploying multiple containers, and Laravel has no built-in answer.
Integration feasibility — low for the package, high for the pattern. Laravel's migrate command already sits on symfony/console, so wrapping it via $this->call('migrate') is trivial. The actual work is driver-specific lock semantics:
pg_try_advisory_lock(key) / pg_advisory_unlock(key) — session-scopedGET_LOCK(name, timeout) / RELEASE_LOCK(name) — connection-scopedsp_getapplock / sp_releaseapplockA Cache::lock() alternative exists but is materially worse for this use case: cache locks carry a TTL, and a migration exceeding that TTL lets a second runner proceed mid-DDL. Database advisory locks die with the session, which is the correct failure semantic.
Technical risk — high, concentrated in abandonment. Last release 2021-10-06, 5 stars, 0 dependents, score 0.055. There is no community validation, no maintainer signal, and Doctrine Migrations major versions since then will likely break its version constraints outright. Adopting the package means adopting a dead dependency that does not even target our framework. Supply-chain exposure is negligible (small surface), but the fork-and-own cost is not.
Key questions for the team:
Stack fit. Laravel 10/11, PHP 8.2+, Postgres 14+ or MySQL 8+, container deploys with ≥2 replicas and a release/init step. The pattern fits; the package does not.
Migration path — do not adopt, reimplement. Recommended: an internal php artisan migrate:locked command that opens a dedicated connection, runs the driver-appropriate try-lock with a bounded wait, invokes migrate --force, and releases in a finally block. Roughly 2–4 engineer-days including tests. Forking the bundle and porting it to Laravel is 5–8 days and leaves us owning a Symfony artifact with a stale Doctrine dependency — strictly worse.
Compatibility. Low friction on our side. The bundle's only real constraint (Doctrine Migrations) is irrelevant to us since Laravel migrations replace it. The underlying Symfony Console layer is already present in every Laravel app, so the command-wrapping approach inherits no new dependencies.
Sequencing.
migrate:locked with configurable timeout.migrate available as a manual escape hatch.Design the lock key per database, not per application — advisory_lock.deploy_migrations or equivalent — so unrelated apps on a shared instance do not collide.
Maintenance. Zero upstream maintenance. Whether we fork or reimplement, we own it permanently; the reimplementation is small enough that ownership is cheap. Add a quarterly review to confirm lock behavior still matches driver versions.
Support. No vendor, no docs beyond a README, no issue activity. Internal SME required. That is unavoidable for either path, but a self-written 200-line command is far easier to hand to a new engineer than a ported Symfony bundle.
Scaling. Advisory locks are per-session and scale fine to N deploy replicas. Two caveats dominate: connection poolers in transaction mode break session locks (lock acquired and released within a pooled transaction, or not at all), and very long migrations hold a lock while blocking other deploys. Cap migration runtime and treat the lock timeout as a hard pipeline failure.
Failure modes to plan for:
Ramp-up. Low for the command itself, moderate for the operational knowledge — driver-specific lock semantics, pooler interactions, and runbook judgment when a deploy stalls. Budget half a day of platform-team onboarding and a written runbook before production enablement.
Recommendation: reference the bundle as prior art, reimplement the pattern internally, and treat the 0.055 score and 2021 release date as disqualifying for direct adoption.
How can I help you explore Laravel packages today?