Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Preference/Rewrite: When a Preference Is Needed Instead of a Plugin

Preference/Rewrite: When a Preference Is Needed Instead of a Plugin

~7 Min. Lesezeit Zuletzt aktualisiert am August 9, 2026

CLAUDE.md, this project's binding coding rules, says it plainly: "use plugins (interceptors) instead of preferences." That's not a style preference, it's a deliberate default for good reason - and block 5 has honored it so far: two plugins (chapters 38/39), zero preferences. This chapter honestly explains where that default hits its limit, before chapter 41 implements this series' one concrete exception.

What is a preference, technically?

A preference is a simple di.xml entry: <preference for="Interface" type="Implementation"/>. Unlike a plugin, there's no code generation and no interceptor wrapping involved - while building the object graph, the DI container simply remembers: "when Interface is requested, hand back an instance of Implementation." It's a complete, one-time swap of the class itself, not a targeted, runtime method interception.

Why plugins are the default

The reason behind the CLAUDE.md rule is purely practical. Plugins are additive and non-destructive: any number of modules can share the same interception point (chapter 43 shows how sortOrder governs the order), the PHP compiler checks method signatures during setup:di:compile and reports incompatibilities immediately, and disabling one module never accidentally takes another module's entire logic down with it. A preference, by contrast, is an all-or-nothing swap: exactly one implementation wins per interface (chapter 42 explores what that means in a conflict).

The three real exceptions

  1. The target method is final or not publicly reachable - an interceptor would have to override it through inheritance, and PHP outright forbids that for final methods with a fatal error, and for private/protected methods altogether (chapter 38). No plugin can reach here, no matter what the use case looks like.
  2. Constructor behavior needs to change - different default values, a different injected dependency, a fundamentally different object setup. Plugins never intercept __construct() at all, regardless of final/private.
  3. The entire implementation behind an interface needs to be swapped, not just one method adjusted - for example because a third-party module implements an interface in a way that's fundamentally incompatible with your own process, not just off in one spot.

Preview: chapter 41's concrete example

Chapter 41 uses the first case: a third-party extension already licensed in this project for ERP integration of the B2B special conditions (chapter 22) ships a calculation method the vendor deliberately declared final - and the requirement is still to combine its result with project-specific logic.

Tipp: This series' checking order in one sentence: first ask whether a matching event exists (observer, chapters 29-37), then check whether a method interception is enough (plugin, chapters 38-39), and only reach for a preference once both are technically off the table. That order isn't arbitrary - it rises exactly in step with how much the intervention can clash with future Magento and third-party updates.

Achtung: Before writing a preference: really make sure no plugin-capable hook exists. In practice, many developers reach for a preference because they know the pattern from older Magento 1 projects ("rewrites") - not because it's technically required. Every preference that could, in hindsight, have been solved as a plugin is unnecessary compatibility risk taken on for nothing (chapter 42).