Change default tracking policy for "deferred explicit" #7515

Open
opened 2026-01-22 15:52:45 +01:00 by admin · 3 comments
Owner

Originally created by @Pierstoval on GitHub (Jun 2, 2025).

Current default Tracking policy is set to "deferred implicit", has been since the beginning, meaning all objects retrieved from repositories are stored in the UnitOfWork for potential updates/deletions for the next call to em->flush or uow->commit

However, I stopped counting the amount of Symfony+Doctrine projects I worked on that suffered from that on two specific levels:

  • Performances suffer when big loads of entities are loaded because of all the comparisons, entities in memory, etc.
  • Side-effects are largely numerous because one single $em->flush() will trigger all the cascading checks for entities updates and deletions

Whenever we encounter performance issues for that, the usual solution is to run the updates in batches, but even then, sometimes you have a batch of entities on which only a percentage need to be actually updated, meaning we lose performances on all other changesets checks and comparisons.

And for the side-effects, I blame the default anemic-objects behavior that most documentations have been recommending for years, because updates on anemic entities can happen anywhere without validation, and the main place I found these were when Symfony Forms were used with Doctrine Entities.
Even though Symfony Forms trigger validation on submit to ensure "one will not persist it", a big bunch of legacy apps still use kernel or doctrine events for other kinds of usages that might call a flush() somewhere.
It can definitely happen in 3rd-party libs, even though the most popular ones usually take care, but we're never sure, will never be, and all the legacy apps in the world using the old-recommended event-based coding practice will encounter this some day. Even EasyAdmin forgot to call "persist()" on update at some point, meaning the whole "Doctrine persists by default" is deeply anchored to devs' minds, even the ones of the most talented and active in the Symfony ecosystem.

Changing the default tracking policy in a major version, and adding a migration path (i.e "A deprecation message recommending to set the annotation manually on each entity", maybe followed by some static analysers, hopefully) will help devs understand more how Doctrine works, and if they decide to keep the "implicit" behavior, they will know what to expect, especially if they migrated using Rector or similar tools that enforce them to explicitly define the PHP attribute to configure the tracking policy.
Conjointedly, the policy could be configurable directly in the Entity annotation in a minor version instead of having its own attribute, to help for better discovery, curiosity and understanding of this concept.

Of course this sounds like a big change, but it's quite as big as the removal of single-entity flush, or removal of the "notify" strategy in ORM v4, for example, but if this is ever accepted at some point, I'm willing to help working on it, since I've been promoting this for a long time.

Originally created by @Pierstoval on GitHub (Jun 2, 2025). Current default Tracking policy is set to "deferred implicit", has been since the beginning, meaning all objects retrieved from repositories are stored in the UnitOfWork for potential updates/deletions for the next call to em->flush or uow->commit However, I stopped counting the amount of Symfony+Doctrine projects I worked on that suffered from that on two specific levels: * Performances suffer when big loads of entities are loaded because of all the comparisons, entities in memory, etc. * Side-effects are largely numerous because one single `$em->flush()` will trigger all the cascading checks for entities updates and deletions Whenever we encounter performance issues for that, the usual solution is to run the updates in batches, but even then, sometimes you have a batch of entities on which only a percentage need to be _actually_ updated, meaning we lose performances on all other changesets checks and comparisons. And for the side-effects, I blame the default anemic-objects behavior that most documentations have been recommending for years, because updates on anemic entities can happen anywhere without validation, and the main place I found these were when Symfony Forms were used with Doctrine Entities. Even though Symfony Forms trigger validation on submit to ensure _"one will not persist it"_, a big bunch of legacy apps still use kernel or doctrine events for other kinds of usages that _might_ call a `flush()` somewhere. It can definitely happen in 3rd-party libs, even though the most popular ones usually take care, but we're never sure, will never be, and all the legacy apps in the world using the old-recommended event-based coding practice will encounter this some day. Even EasyAdmin forgot to call "persist()" on update at some point, meaning the whole "Doctrine persists by default" is deeply anchored to devs' minds, even the ones of the most talented and active in the Symfony ecosystem. Changing the default tracking policy in a major version, and adding a migration path (i.e "A deprecation message recommending to set the annotation manually on each entity", maybe followed by some static analysers, hopefully) will help devs understand more how Doctrine works, and if they decide to keep the "implicit" behavior, they will know what to expect, especially if they migrated using Rector or similar tools that enforce them to explicitly define the PHP attribute to configure the tracking policy. Conjointedly, the policy could be configurable directly in the `Entity` annotation in a **minor version** instead of having its own attribute, to help for better discovery, curiosity and understanding of this concept. Of course this sounds like a big change, but it's quite as big as the removal of single-entity flush, or removal of the "notify" strategy in ORM v4, for example, but if this is ever accepted at some point, I'm willing to help working on it, since I've been promoting this for a long time.
Author
Owner

@greg0ire commented on GitHub (Jun 4, 2025):

Hi and thanks for the detailed issue!

Of course this sounds like a big change, but it's quite as big as the removal of single-entity flush, or removal of the "notify" strategy in ORM v4, for example, but if this is ever accepted at some point, I'm willing to help working on it, since I've been promoting this for a long time.

This sounds a bit scary… how about instead we somehow, if possible, make the tracking policy configurable on an application per application basis in a minor release, and then, if more people feel it should be the default, change the symfony recipe to configure it to deferred explicit. This way, we could get feedback on this change without affecting existing applications. We could then configure changing or even removing the default value.

@greg0ire commented on GitHub (Jun 4, 2025): Hi and thanks for the detailed issue! > Of course this sounds like a big change, but it's quite as big as the removal of single-entity flush, or removal of the "notify" strategy in ORM v4, for example, but if this is ever accepted at some point, I'm willing to help working on it, since I've been promoting this for a long time. This sounds a bit scary… how about instead we somehow, if possible, make the tracking policy configurable on an application per application basis in a minor release, and then, if more people feel it should be the default, change the symfony recipe to configure it to deferred explicit. This way, we could get feedback on this change without affecting existing applications. We could then configure changing or even removing the default value.
Author
Owner

@beberlei commented on GitHub (Jun 5, 2025):

I doubt this will happen, its too big a change. Single entity flush and notify policy removal did not require adding code, it would mostly just continue to work.

Your proposal would require to ausir code for adding persist at the right places and that could be daunting.

@beberlei commented on GitHub (Jun 5, 2025): I doubt this will happen, its too big a change. Single entity flush and notify policy removal did not require adding code, it would mostly just continue to work. Your proposal would require to ausir code for adding persist at the right places and that could be daunting.
Author
Owner

@Pierstoval commented on GitHub (Jun 11, 2025):

I doubt this will happen, its too big a change. Single entity flush and notify policy removal did not require adding code, it would mostly just continue to work.

I've seen projects migrating from this having a huge lot of issues because they relied on single entity flushes a lot, which triggered architectural changes in the app itself. This happens.

This sounds a bit scary… how about instead we somehow, if possible, make the tracking policy configurable on an application per application basis in a minor release, and then, if more people feel it should be the default, change the symfony recipe to configure it to deferred explicit. This way, we could get feedback on this change without affecting existing applications. We could then configure changing or even removing the default value.

Yep, this seems like a good migration path! A message saying doctrine.orm.entity_managers.default: not specifying the "default_tracking_policy" option is deprecated, it is currently set to "deferred_implicit" and will default to "deferred_explicit" in version X, updating the default recipe to contain the deferred_implicit value with a link to the Doctrine ORM docs about tracking policies, and setting it to deferred_explicit in a next-gen recipe would help both discovering AND documenting said option.

@Pierstoval commented on GitHub (Jun 11, 2025): > I doubt this will happen, its too big a change. Single entity flush and notify policy removal did not require adding code, it would mostly just continue to work. I've seen projects migrating from this having a huge lot of issues because they relied on single entity flushes a lot, which triggered architectural changes in the app itself. This happens. > This sounds a bit scary… how about instead we somehow, if possible, make the tracking policy configurable on an application per application basis in a minor release, and then, if more people feel it should be the default, change the symfony recipe to configure it to deferred explicit. This way, we could get feedback on this change without affecting existing applications. We could then configure changing or even removing the default value. Yep, this seems like a good migration path! A message saying `doctrine.orm.entity_managers.default: not specifying the "default_tracking_policy" option is deprecated, it is currently set to "deferred_implicit" and will default to "deferred_explicit" in version X`, updating the default recipe to contain the `deferred_implicit` value with a link to the Doctrine ORM docs about tracking policies, and setting it to `deferred_explicit` in a next-gen recipe would help both discovering AND documenting said option.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#7515