Description
#22742 changed the constraints in the 3.4 branch to allow 4.0.x version of the other components. After that, I had a lengthy discussion with @nicolas-grekas on Slack challenging it. We now think it is time to share our thoughts outside the core team (and with core team members not connected at this time) to involve the whole community.
Note: for the whole discussion, I will talk about components, but this also applies to bridges and bundles developed in the symfony/symfony repo. It is just shorter than repeating
components, bridges and bundles
all the time.
The whole point of this discussion is to decide how the requirements of the of inter-component dependencies should look like in the 3.4 branch. There is 3 possible options:
^3.2
, not allowing any usage of 4.x dependencies^3.2 || ~4.0.0
, allowing only the 4.0.x versions of deps, but not 4.1+ (what Allow individual bridges, bundles and components to be used with 4.0 #22742 did, and what we did for 2.8 as well)^3.2 || ^4.0
, allowing the whole 4.x releases, relying on the BC promise of 4.x
Note: example above assume that the lowest bound of the existing 3.3 constraint is
3.2
. It can be something else for each requirement, so things should be adapted above accordingly.
The third option was rejected by @nicolas-grekas because it hurts our promise that the Symfony code will never trigger deprecations by itself (i.e. calling a non-deprecated API from your own code does not make Symfony call a deprecated API internally on its own), as it precisely relies on BC layers.
I'm arguing that we should stick with the first option, because of the way Composer works and because of our ecosystem change with Flex.
Regarding Composer, what you need to remember is how it chooses the installed version: it always selects the highest version available which matches your constraints. So if you require ^3.2 || ~4.0.0
, it will always try to use a 4.0.x release first. This has 2 drawbacks:
- 4.0 brings BC breaks (this is why it is named 4)
- 4.0.x is not a LTS version, meaning it will go unmaintained much before 3.4
Regarding the ecosystem change, the big difference is what Symfony users will tell Composer when they want to use our 3.4 LTS version (because they have not yet migrated their code to 4.x APIs for instance):
- in the symfony-standard way, they would tell "Give me Symfony 3.4" (i.e.
composer require symfony/symfony ^3.4
), and they would get the 3.4 version of the whole Symfony code - in the flex way, they would tell "Give me FrameworkBundle 3.4" (i.e.
composer require symfony/framework-bundle ^3.4
), and they would expect to get 3.4 version of Symfony. But the options above may break this assumption.
What is important to remember is that people using symfony/framework-bundle
as a dependency actually spend most of their time interacting with transitive dependencies brought in by FrameworkBundle:
- they write route definitions (
symfony/router
) - they write controllers (
symfony/http-kernel
) - they handle requests and responses in their controller (
symfony/http-foundation
) - they write service definitions (
symfony/dependency-injection
) - they trigger or listen to events (
symfony/event-dispatcher
) - ...
If we go with the option 2, a new Symfony 3.4 project would automatically get the 4.0 version of all the components I listed just above (and a few others), until another rule forbids to use version 4. and this is very bad, because it means people will not receive BC layers anymore, which basically breaks our BC promise.
Note: FrameworkBundle is not the only package having mandatory requirements on other Symfony packages, but it was the best example for this case as it is the entry point of using Symfony in Flex.
This could also affect external projects relying on Symfony components, as they could also be relying on transitive deps (symfony/http-kernel
bringing in symfony/http-foundation
).
Note: Things can become even worse if
symfony/framework-bundle
removes the explicit require onsymfony/http-foundation
and relies onsymfony/http-kernel
bring it to the user instead, as bring in 4.0.x for HttpKernel then allows bring in any 4.x version for HttpFoundation, which goes to option 3.
I only see 2 ways to avoid this issue:
- stick to option 1, meaning that a dependency between Symfony components cannot require a higher major version than the current one (allowing lower major versions of them is fine, and 4.x components should actually all allow 3.4 version of their deps as the 3.4 version will have all the features they need)
- teach people to never rely on transitive deps for the packages they directly use in their project, which means that the Symfony skeleton should list much more packages by default (duplicating the FrameworkBundle deps without allowing 4.0.x).
Due to the effect on DX, I strongly suggest sticking to option 1. But the call is now on the community, to decide about option 1, 2 or 3 above.
Note that for dev requirements, we should allow bringing in 4.0 versions, unless we also have a conflict rule forbidding them, because this helps ensuring that users don't end up in non-working cases (as optional deps cannot be enforced to stay on 3.x if we don't have conflict rules forbidding them, as we don't control the requirement bringing them in).