What Are Preferences?
Preference Declaration
A preference replaces a class entirely via DI:
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<preference for="Magento\Catalog\Model\Product"
type="Vendor\Module\Model\Product"/>
</config>
How It Works
// Original class
namespace Magento\Catalog\Model\Product;
class Product {
public function getName() { return $this->name; }
}
// Your replacement
namespace Vendor\Module\Model;
class Product extends \Magento\Catalog\Model\Product {
public function getName() {
return parent::getName() . ' (Modified)';
}
}
When ObjectManager creates Product, it actually creates your Vendor\Module\Model\Product.
Interface Preferences
<preference for="Magento\Catalog\Api\ProductRepositoryInterface"
type="Vendor\Module\Api\ProductRepository"/>
This replaces the default implementation of the interface.
When to Use Preferences
Appropriate Use Cases
- Replacing default implementations of interfaces
<preference for="Magento\Search\Model\SearchEngineInterface"
type="Vendor\CustomSearch\Model\SearchEngine"/>
- Adding behavior to classes without events/plugins
// Override save() to add custom validation
public function save() {
$this->validateCustomFields();
return parent::save();
}
- Fixing bugs in core classes (temporary, then upgrade)
public function getData($key = null) {
// Fix for known bug
if ($key === 'special_field') {
return $this->fixSpecialField();
}
return parent::getData($key);
}
- Adding methods to existing classes
public class ExtendedProduct extends Product {
public function getExtendedData() {
// New method not in original class
}
}
Not Appropriate For
- Modifying behavior that plugins can handle
- Adding side effects (use observers)
- Temporary overrides (use plugins or config)
- Replacing core classes without clear need
Preference Limitations and Risks
Only One Preference Per Class
Only one preference can exist for any class or interface. If two modules define preferences for the same class, one wins based on module load order.
<!-- Module A -->
<preference for="Magento\Catalog\Model\Product" type="Vendor\A\Product"/>
<!-- Module B -->
<preference for="Magento\Catalog\Model\Product" type="Vendor\B\Product"/>
<!-- Only one will be used — unpredictable which one wins -->
Upgrade Fragility
Preferences override the entire class. When Magento upgrades and changes the original class, your preference may break.
// Your preference from Magento 2.4.3
public function save() {
// Override based on original implementation
$this->validate();
parent::save();
}
// Magento 2.4.4 changes save() internals
// Your preference breaks silently or throws errors
Cannot Compose
Preferences cannot be composed. You cannot stack multiple preferences on the same class.
Testing Difficulty
Preferences make testing harder because they replace the entire class. Unit tests may need to mock the full class instead of just the interface.
Performance Impact
Preferences affect all instances of a class across the entire application, not just specific method calls.
Plugin Advantages Over Preferences
Composability
Multiple plugins can coexist on the same class:
<!-- Module A -->
<type name="Magento\Catalog\Model\Product">
<plugin name="module_a" type="Vendor\A\Plugin" sortOrder="10"/>
</type>
<!-- Module B -->
<type name="Magento\Catalog\Model\Product">
<plugin name="module_b" type="Vendor\B\Plugin" sortOrder="20"/>
</type>
Both plugins execute without conflict.
Granular Control
Plugins target specific methods:
<type name="Magento\Catalog\Model\Product">
<plugin name="enhance_name" type="Vendor\Plugin\ProductNamePlugin"/>
</type>
Only getName() is affected; other methods remain untouched.
Easy Disabling
<type name="Magento\Catalog\Model\Product">
<plugin name="third_party_plugin" disabled="true"/>
</type>
Disable without removing code.
Upgrade Safety
Plugins survive Magento upgrades because they wrap methods rather than replacing classes. If the original method changes, the plugin continues to work.
Comparison Table
| Aspect | Plugin | Preference |
|---|---|---|
| Scope | Method-level | Class-level |
| Multiple | Yes (composable) | No (one per class) |
| Granularity | Specific methods | Entire class |
| Upgrade safety | High | Low |
| Disabling | Easy (disabled flag) | Requires removing |
| Can add methods | No | Yes |
| Can skip methods | Yes (around) | Yes (override) |
| Testability | High | Medium |
Practice Problems
Two modules both define a preference for the same interface. How do you resolve this?
Quiz
1. How many preferences can exist for a single class?
2. What is a key advantage of plugins over preferences?
3. When should you use a preference?
4. What happens when two modules define preferences for the same class?
Flashcards
Question
How many preferences per class?
Click to reveal answer
Answer
Only one preference can exist for any class or interface
Question
Can multiple plugins coexist on the same class?
Click to reveal answer
Answer
Yes, plugins are composable with sort_order controlling execution
Question
Which is more upgrade-safe, plugins or preferences?
Click to reveal answer
Answer
Plugins — they wrap methods rather than replacing entire classes
Question
Can a plugin add new methods to a class?
Click to reveal answer
Answer
No, plugins can only intercept existing public methods
Question
When is a preference appropriate?
Click to reveal answer
Answer
When replacing the default implementation of an interface
Revision Notes
Key Takeaways
- 1. Preferences replace an entire class via DI; only one preference per class/interface
- 2. Plugins are composable — multiple plugins can target the same class
- 3. Preferences are fragile across upgrades; plugins survive upgrades
- 4. Use preferences to replace interface implementations, not for method-level changes
- 5. Plugins offer granular control, easy disabling, and better testability
- 6. Prefer plugins over preferences unless you need full class replacement
Interview Tips
- • Explain the fundamental difference between preferences and plugins
- • Discuss why preferences are risky for upgrades
- • Give examples of when a preference is actually appropriate
- • Explain how to resolve preference conflicts between modules
Cheat Sheet
Plugin vs Preference Cheat Sheet
Preference:
- Replaces entire class
- Only one per class/interface
- Use for interface implementations
- Fragile on upgrades
Plugin:
- Wraps specific methods
- Multiple composable
- Use for method modification
- Upgrade-safe
Decision:
- Need to replace implementation? -> Preference
- Need to modify method behavior? -> Plugin
- Need to add side effects? -> Observer
- Need to add methods? -> Preference (carefully)