laravel composer upgrade maintenance

Upgrading from 13.6.1

robdesilets

Senior Developer · 2024-01-23

Laravel Company

Hi All, I did my daily composer update today and something caught my eye: Upgrading laravel/framework (v13.16.1 => 13.x-dev 2107d3d): Extracting archive Usually it will go from a version to a [higher] version not some weird dev branch. To my knowledge I have not changed anything in my environment that would cause me to grab a developer branch and was wondering wh...

Composer occasionally upgrades a framework package to a development branch because of branch-alias definitions, minimum-stability flags, or conflicting constraints in your lock file. The shock is understandable: you expected a stable minor bump, but Composer chose a branch instead. This post explains why it happens, how to inspect the resolution, and how to regain predictable updates.

Resolution Audit

Run composer why laravel/framework and composer depends laravel/framework to see which packages require the framework and at which versions. Inspect composer.lock diff from the last successful update to see what changed. If a branch-alias shifts or a dev constraint slips in, it will surface here.

Stability Flags

minimum-stability and prefer-stable together control whether Composer can choose dev branches. A project configured with minimum-stability dev and prefer-stable false may follow branch updates more aggressively than intended. Set prefer-stable to true and use explicit version constraints when relying on stable releases.

Composer Update Strategy

Use composer update vendor/package --with-all-dependencies to constrain updates to the packages you care about. For Laravel framework updates specifically, run update on laravel/framework only and inspect the proposed versions before accepting them. Lock tools such as composer-plugin-api help enforce bounded update windows.

Conclusion

Unexpected branch upgrades are a configuration and visibility problem. Audit dependencies, enforce stability preferences, and review lock-file changes before deploying framework updates.

Related Posts

These related posts cover framework maintenance, runtime performance, and database operation safety.

Why Composer Might Select a Dev Branch

Composer's version constraints follow semantic versioning rules by default. When your composer.json specifies "laravel/framework": "^13.16", Composer looks for the highest version matching that constraint. In some cases, branch aliases in the framework's composer.json map the current dev branch to a version like "13.16.1-dev", causing the behavior you observed. This typically happens after a minor release when maintainers tag a dev branch to help early adopters test upcoming patches.

To prevent accidental dev installs, use exact version constraints in production: "13.16.1" instead of "^13.16". Or configure composer.json to require stability "stable" and prefer stable releases. Running composer update with --prefer-stable also biases selection toward tagged releases. Always review the diff in a temporary branch before merging composer upgrades, and consider using Dependabot or Renovate for controlled, automated dependency updates with changelog summaries.

Impact on Laravel Ecosystem Packages

Upgrading to a dev branch can cause mismatches across the Laravel ecosystem. Spatie packages, Laravel Sanctum, and other first-party extensions often have their own version bounds. A dev framework install may trigger cascading upgrades to dev branches of related packages, amplifying your risk. Use composer prohibits or the composer why-not command to understand constraint conflicts. If you suspect a specific package pulled in the dev branch, run composer update vendor/package --with-all-dependencies to inspect the resolution plan before applying it.

For more context on Laravel version management and deployment considerations, see Laravel Octane benchmark comparing Swoole, OpenSwoole, RoadRunner, FrankenPHP and Difficulty scaling Laravel Horizon across multiple instances (ECS / Auto Scaling), which discuss how framework and runtime versions interact with performance and infrastructure.

Stable Upgrade Workflow

Before touching composer.json, back up your current lock file and database. In a feature branch, run composer outdated to see available updates. Update framework and first-party packages together to avoid mismatched versions. Run php artisan migrate and php artisan test after updating. Pay special attention to Laravel's upgrade guide for each major version increment; even patch releases occasionally include deprecations.

If you must revert, composer is reversible: checkout the previous composer.lock and run composer install. In CI, pin the PHP version and Composer version to avoid environment drift. For zero-downtime deploys, use Laravel's rolling restart strategies with Octane or traditional Forge deployments.

Monitoring Post-Upgrade

After upgrading, monitor error rates, response times, and queue throughput. Laravel Telescope is invaluable for spotting regressions in queries, jobs, and requests. Set up alerts for 5xx spikes and queue job failures. Keep an eye on deprecation warnings in logs; PHP 8.2+ deprecations can become fatal errors in PHP 9.0.

See Laravel Octane benchmark comparing Swoole, OpenSwoole, RoadRunner, FrankenPHP for runtime considerations, Difficulty scaling Laravel Horizon across multiple instances (ECS / Auto Scaling) for infrastructure changes, and Yajra datatables questions for frontend compatibility.

Handling Composer Lock Conflicts

If you work in a team, composer.lock conflicts are common after upgrades. Resolve them by re-running composer update locally with the target platform (PHP version matching production), then committing the regenerated lock file. Use git rerere to remember resolutions for recurring conflicts. In CI, use composer install --no-interaction --prefer-dist to ensure reproducible installs from the lock file.

Long-Term Maintenance Strategy

Adopt a regular dependency maintenance cadence: weekly or monthly composer update --prefer-stable with CI running the full test suite. Automate changelog generation from conventional commits. Tag releases after successful deploy. Keep a sandbox environment for trying risky upgrades before they reach staging. Document known issues and workarounds in an internal wiki.

For infrastructure and runtime upgrades, see Laravel Octane benchmark comparing Swoole, OpenSwoole, RoadRunner, FrankenPHP and Difficulty scaling Laravel Horizon across multiple instances (ECS / Auto Scaling).

Summary and Next Steps

Composer surprises like installing a dev branch usually reflect version constraints or branch aliases rather than corruption. Pin exact versions in production, use prefer-stable, and review diffs before deploying. Adopt a regular dependency maintenance routine with CI coverage, charmaps, and staged rollouts. Document known issues in an internal runbook. Monitor post-upgrade metrics with Laravel Telescope and error tracking. Keep your framework aligned with your infrastructure plans. For performance considerations during upgrades, see Laravel Octane benchmark comparing Swoole, OpenSwoole, RoadRunner, FrankenPHP and Difficulty scaling Laravel Horizon across multiple instances (ECS / Auto Scaling).