ezappslab/laravel-evolver
Laravel Evolver by ezappslab gives Laravel teams a structured, version-aware way to manage evolutionary application and database changes, keeping deployments repeatable, auditable, and rollback-friendly without adding workflow overhead.
Architecture fit. Laravel Evolver v3 is a deployment/versioning orchestration package for Laravel applications: it plans and executes "actions" (data migrations, backfills, one-off tasks), records them with checksums, ties them to application/API version windows, and now provides first-class rollback, locking, and API-version routing. Architecturally it fits applications that already treat deploys as ordered, auditable action batches and that want version-gated API routing inside Laravel's router rather than in a separate gateway. The v3 rewrite moves the package from a collection of loosely coupled helpers (v2's Api\*, Deploy\*, Version\*, Models\Evolution) to a consolidated Deployment\* / Versioning\* / Routing\* model with explicit contracts (ActionRepository, ApplicationVersionResolver). That is a cleaner seam for teams that want to substitute persistence or version sources, but it is a near-total internal reshuffle that will invalidate any custom extensions built against v2 internals.
Integration feasibility. The hard gate is the platform matrix: PHP 8.4, Laravel 12 or 13, Symfony 7 or 8. PHP 8.4 and Laravel 12 are recent; Laravel 13 is not broadly available at the time of this release. Most enterprises on PHP 8.2/8.3 and Laravel 10/11 cannot adopt v3 without a framework upgrade, which turns this from a package bump into a platform programme. The config restructure is broad (actions_path → actions.path, transactions.mode values renamed, versioning.strategy → versioning.application.resolver, API keys regrouped under versioning.api, plus new lock.* keys and EVOLVER_* env vars). Integration is feasible but requires a controlled config migration, not a drop-in update. The preservation of the evolutions table schema and SHA-256 checksums is the single most important compatibility decision: it means v2 history remains readable and avoids a data migration, which materially reduces cutover risk.
Technical risk.
handle() → up(), requiredUntil() → expiresAt(), filename identity changed to snake_case. Every unexecuted action must be converted; every executed action must be left untouched.safety.fail_on_changed_action is on. This is a strong safety property but a sharp operational edge: a well-intentioned cleanup of an old action file can halt deployments, and those old actions cannot be rolled back.none resolver (now the default), every action applies regardless of declared version window. Teams that relied on v2's versioning.required to enforce windows must explicitly configure a resolver or they will silently lose gating. This is a behavioural change hiding behind a config default.ApiVersionRouteRegistrar::routes() is replaced by Route::apiVersion('v2', $routes, $middleware = ['api']), and ResolveApiVersion is registered globally. The global registration is scoped to the API base path, but it is still a global middleware whose behaviour depends on correct versioning.api.resolvers.url.base_path configuration. Header-based versioning returns 400 on missing header and 404 on unsupported version — a contract change for API consumers.evolver:api-status and API version lifecycle states are gone. Any runbook, dashboard, or CI script that parses this output must be rewritten.lock.store and lock.seconds; a misconfigured or non-atomic store reintroduces concurrent-deploy risk. LockException gives a clear failure, but the failure mode is a blocked deploy.Key questions.
versioning.required or v2 version-window enforcement? If so, which resolver (config, file, json, git, header, or class) replaces it, and who owns that decision per application?lock.store in each environment, and is it atomic and shared across all deploy runners (including local/CI)?evolver:api-status or the removed Api\* / Deploy\* / Version\* namespaces?down()), and what is the manual recovery plan when rollback refuses them?Stack fit. The package belongs in the deploy pipeline of Laravel services that already need ordered, auditable one-off actions and version-gated APIs. It is a poor fit for teams stuck on PHP 8.2/8.3 or Laravel ≤11 until those platforms are upgraded; for those teams, v2 remains the practical choice and v3 should be treated as a roadmap item, not a near-term upgrade. Because v3 is Laravel-native (middleware, facade, Process facade, config publishing, testing fake), it integrates cleanly with existing Laravel conventions and does not introduce a separate runtime or sidecar.
Migration path. The release notes prescribe a sane five-step path; a TPM should turn it into a gated programme:
evolver-config, and mechanically map v2 keys to v3 using the published table. Treat versioning.strategy → versioning.application.resolver and the API key regroup as deliberate decisions, not find-and-replace.evolutions table; do not republish the migration on existing installs. Confirm checksums still validate against v2 history in a staging environment.handle() → up(), requiredUntil() → expiresAt(), add down() where reversible. Freeze executed action files and add a CI or pre-deploy check that fails if an executed action's checksum changes.ApiVersionRouteRegistrar::routes() with Route::apiVersion(), configure the API base path and resolver, and exercise header-based versioning (400/404 paths) if adopted.evolver:deploy --pretend in staging, then a controlled deploy with lock store configured, then validate evolver:status and a rollback rehearsal on a reversible batch.Compatibility. The retained evolutions schema and checksum format is the key compatibility bridge; everything above the table (namespaces, contracts, config shape, routing API) is breaking. Custom code extending Api\*, Deploy\*, Version\*, EvolutionRepository, or VersionResolver must be rewritten against Deployment\*, Versioning\*, Routing\*, Contracts\ActionRepository, and Contracts\ApplicationVersionResolver. Plan for a feature branch and a parallel staging environment rather than an in-place upgrade on the mainline.
Sequencing. Do platform upgrades first, then config and action conversion, then routing, then lock/rollback enablement. Do not combine the platform upgrade and the Evolver upgrade in one change window: if a deploy fails, you want to know whether it was PHP/Laravel or the new deployment engine. Rollback should be rehearsed before it is needed, not discovered during an incident.
Maintenance. v3 is a rewrite, so expect a period of stabilisation: config edge cases, resolver behaviour, and lock-store tuning will surface in the first weeks. The quality signals are strong — 100% line and type coverage, PHPStan, Rector, Pint through composer lint — which lowers the risk of latent defects and makes upstream contributions or patches more tractable. The consolidated namespaces and explicit contracts also reduce the long-term maintenance surface compared with v2's scattered namespaces.
Support. Two support concerns stand out. First, the frozen-executed-action rule needs a documented operational policy, not just a config flag: who may edit action files, how changes are detected, and what to do when safety.fail_on_changed_action blocks a deploy. Second, the removed evolver:api-status command and API lifecycle states mean existing runbooks and dashboards need updating; support staff will otherwise follow dead instructions during an incident. The clearer exception set (ActionExecutionException, LockException, RepositoryUnavailableException, MissingApiVersionException) is a genuine supportability improvement because failures now name the failing action and the actions completed before it.
Scaling. Locked planning and execution under one atomic cache lock is the right primitive for concurrent deploy runners, but it centralises correctness in the cache store: the lock must be shared, atomic, and reachable from every runner, including CI and operator laptops. lock.seconds needs a value longer than the slowest expected batch, or a long deploy will lose its lock mid-run. The Octane-safe per-request ApiVersionContext is a good scaling detail for high-throughput APIs. Checksum verification during planning (without loading executed actions) keeps status/deploy cost proportional to pending work rather than total history, which scales better than v2.
Failure modes. The important ones:
fail_on_changed_action is on; old actions cannot be rolled back. High-impact, easy to trigger by editing a file.LockException halts the deploy; misconfigured store silently removes serialisation.none, version windows are ignored and all actions apply; a silent behavioural change rather than a loud failure.Ramp-up. Expect meaningful onboarding cost even for experienced Laravel teams: the action lifecycle (up()/down(), filename identity, checksum semantics), the resolver matrix (none, config, file, json, git, header, class name), the transaction modes (action, batch, none), and the lock configuration all need to be understood together. The testing fake (Evolver::fake(), assertExecuted, assertRolledBack, add/touch/forget) is the strongest ramp-up asset in the release: it lets teams write deploy-behaviour tests before touching production, and it should be the first thing introduced in the migration plan. Budget for a half-day to a day of engineering onboarding per team, plus a platform-upgrade track for teams not yet on PHP 8.4 / Laravel 12.
How can I help you explore Laravel packages today?