When to Release a New Major Package Version
Laravel package maintainers should release a new major version when a change breaks something they promised users would keep working. Those promises…
When you maintain a Laravel package, its public API includes anything users can reasonably call, configure, register, or rely on. This includes PHP methods, facades, service-provider bindings that register package services with Laravel, configuration keys, Artisan commands, and documented behavior. Laravel’s package documentation explains how packages connect to Laravel with service providers and facades.
To deprecate an API, provide a supported replacement, warn users about the old entry point, and keep it working while they migrate. A deprecation warning announces that plan while the API remains available as users migrate. For packages that follow Semantic Versioning, or SemVer, mark public functionality as deprecated in a minor release and remove it in a major release. SemVer sets these version boundaries but does not say how long users must have to migrate.
Give users a working replacement before asking them to stop using the old API. If the replacement changes behavior, document those differences alongside the migration instructions.
For a PHP method, keep the old method’s signature and return behavior during the transition. Make it delegate to the new method so the package has one implementation to maintain:
/**
* @deprecated since acme/reports 1.8.0; use exportJson() instead.
*/
public function toJson(): string
{
trigger_error(
'Acme\\Reports\\Report::toJson() is deprecated since 1.8.0; use exportJson(). It is planned for removal in 2.0.0.',
E_USER_DEPRECATED,
);
return $this->exportJson();
}
The package name and versions in this example are illustrative. In your message, name the release that introduces the deprecation and the removal version you plan to meet.
PHP defines E_USER_DEPRECATED as a deprecation level for warnings raised by user code. Pass it to trigger_error() as the warning type. Emit the warning only from the old method. If the replacement also warns, a call through the old method can produce duplicate warnings. Name the deprecated call and its replacement in the message, along with the relevant versions.
The warning tells users about the change; the forwarding method keeps old code working. Preserve the old method’s accepted inputs, signature, return type, and relevant behavior for the announced migration period. Test both the replacement and the forwarding method.
Before announcing a deprecation, review more than PHP methods. A package’s public API can also include a facade name, a container binding, a published configuration key, a command, or a documented default. The package’s README, examples, tests, and upgrade instructions can also create expectations about behavior.
List the old and new forms, then decide how the package will keep each old form working. A method can forward to a replacement, while an old binding can remain as an alias. For configuration, the reader can check the new key before falling back to the old one. Warn when users call an entry point the package controls.
Some usage cannot be detected reliably. For example, when users read a configuration key directly through Laravel’s configuration helper, the package may not see that access. In that case, document the old key and migration path, and state that the package cannot emit a warning for every use.
For a package that follows SemVer after version 1.0.0, a minor release marks public functionality as deprecated. A major release removes it in a change that breaks backward compatibility. The SemVer specification defines these version increments but does not require a particular number of releases or months between deprecation and removal.
This example shows one possible schedule:
| Release | Maintainer action | User impact |
|---|---|---|
1.7.x |
Support the old API. | Existing calls continue to work. |
1.8.0 |
Add the replacement, mark the old API deprecated, and forward old calls to the replacement. | Existing calls still work and emit a warning. |
1.9.x |
Keep the forwarding method and update migration instructions and examples. | Users have another compatible release line in which to migrate. |
2.0.0 |
Remove the old API and document the breaking change. | Users must migrate or remain on the compatible major version. |
Choose the interval based on how many users depend on the API, how hard it is to migrate, how often users update, and whether they can automate the change. SemVer does not establish a minimum grace period, so publish your policy and follow it.
Tell users which package release introduced the warning. A Composer constraint such as ^1.0 allows compatible 1.x updates, but a lock file keeps an application on its recorded versions until someone updates its dependencies. Composer documents how version constraints affect allowed releases and how install and update handle locked and newly resolved versions.
Release notes should name the replacement and say how long users can keep using the old entry point. For example:
Deprecated:
Report::toJson()
Report::toJson()is deprecated as of1.8.0. UseReport::exportJson()instead. The old method remains available and delegates to the replacement during the1.xseries. Removal is planned for2.0.0. See the migration instructions for behavior differences and examples.
Use a removal version only when it reflects a real plan. If you have not chosen one, say that removal is planned for a future major release and publish the target when you decide it. For a configuration key or binding, name both the old and new forms and explain which one the package continues to accept.
Update the release notes and API documentation. Update the examples too. New examples should use the replacement; show the deprecated form only when explaining the migration.
Test that the replacement works and that the deprecated entry point still returns the expected result. Also check that the old entry point emits the intended deprecation warning. PHP error reporting and custom error handlers affect how warnings appear, so test in an environment that resembles the Laravel applications that use the package. PHP documents how trigger_error() interacts with error handling.
If the package supports PHP versions earlier than 8.4, do not rely on PHP’s #[\Deprecated] attribute as its only warning mechanism. PHP introduced that attribute in 8.4 for supported declarations; the PHP manual describes its availability and use. A package supporting earlier PHP versions should use a compatible strategy, such as trigger_error(), and test that strategy across its supported versions.
For Laravel integration points, test the package through its supported paths, such as service-provider discovery or facade registration. Keep the removal version, migration instructions, tests, and release notes aligned so users do not encounter a surprise break.
Give Vroni a GitHub issue, bug report, spec, or rough idea. It reads the repo, plans the change, writes code, runs checks, and works toward a review-ready pull request.
Take a look at vroni.com