Magento 2 Plugins, Observers and Preferences: Which to Use

Magento 2 Plugins, Observers and Preferences: Which to Use

May 18, 2026 · By Magento Company
Magento 2 Plugins, Observers and Preferences: Which to Use

Magento gives you three main ways to change core behaviour without editing core files: plugins (interceptors), observers (events) and preferences (class substitution). Choosing the wrong one is behind most fragile customisations we unpick in audits - the rule of thumb is simple, but the details matter.

Plugins: The Default Choice

A plugin intercepts a public method call with before, after or around logic:

<type name="Magento\Catalog\Model\Product">
    <plugin name="acme_product_name_suffix"
            type="Acme\Catalog\Plugin\ProductNameSuffix"/>
</type>
public function afterGetName(Product $subject, string $result): string
{
    return $result . ' (VAT included)';
}
  • before can modify arguments
  • after can modify the return value
  • around can wrap, modify both, or skip the original entirely (call $proceed() or don’t)

Plugins are the most surgical tool: multiple modules’ plugins chain together cleanly via sortOrder, they work on any public method of a non-final class, and they are the mechanism least likely to conflict with other extensions.

Limits: plugins cannot touch private, protected, static or final methods, or classes instantiated before interception is possible. And an around plugin that forgets $proceed() silently replaces core behaviour - use before/after unless you genuinely need around.

Observers: Reacting to Events

Observers listen for events the core (or a module) explicitly dispatches:

<event name="sales_order_place_after">
    <observer name="acme_send_to_erp"
              instance="Acme\Erp\Observer\SendOrder"/>
</event>

Use observers when you want to react to something that happened - order placed, customer registered, product saved - rather than change how it happens. The contract is explicit: an event is a documented extension point that survives upgrades better than intercepting an arbitrary method.

Limits: you can only observe events that exist. If the core does not dispatch an event where you need one, you are back to plugins (or dispatching your own event from a plugin).

Preferences: The Sledgehammer

A preference replaces one class with another globally:

<preference for="Magento\Catalog\Model\Product"
            type="Acme\Catalog\Model\CustomProduct"/>

Every place the object manager creates the original class, it creates yours instead. This is powerful and dangerous in equal measure:

  • Only one module can win a preference on a given class - two extensions preferring the same class is a guaranteed conflict
  • Your replacement must satisfy the original’s full contract, including constructor arguments, or DI compilation fails
  • Core upgrades that change the original class can silently break your subclass

Preferences are appropriate for substituting an interface implementation you control end-to-end, or replacing a service with a genuinely alternative implementation. They are almost never right for “tweak this one method” - that is a plugin.

The Decision Rule

  1. Is there an event for it? Use an observer.
  2. No event, but a public method? Use a plugin, before/after first.
  3. Genuinely replacing an implementation? Consider a preference - and document why a plugin could not do it.

Following that order keeps your customisations composable with other extensions and survivable across upgrades. Breaking it is why some stores cannot be upgraded without a month of archaeology.

Development Architecture Magento 2