Summary
Derivative plugins are a powerful feature in Drupal, allowing dynamic generation of plugins from a base class (e.g., a block per Block Content entity).
But beware: their misuse can turn your site into a slow and unstable monster. By generating thousands of plugin definitions (via inline entity reference fields, cascading derivations, etc.), you risk saturating the cache (cache_discovery), triggering costly rebuilds with every change, and making your site unusable.
Solution: Limit derivatives to strictly necessary cases, avoid derivation chains, and favor configurations over dynamic definitions.
Why do plugins make Drupal so powerful?
Drupal owes much of its flexibility to its plugin system, which allows modules to extend native functionalities without modifying the core. Some concrete examples:
- Create a custom block,
- Add a field type, a formatter, or a widget,
- Extend CKEditor (filters, toolbars),
- Integrate SDC components (via YAML converted to plugins),
- Define a view argument type or a filter.
The plugin API provides:
- Automatic discovery of active plugins in modules,
- A caching system to optimize performance.
Key difference with Event Listeners:
- A plugin is configurable (it can include its own configuration form), unlike an event listener.
- A module can provide a plugin for another module without forcing a dependency. If the "parent" module (e.g., CKEditor) is not enabled, the plugin is simply ignored. With event listeners, dependency injection requires the parent module to be active.
Derivative Plugins: A double-edged sword
The principle
Derivatives allow a module to dynamically generate plugins from a base class.
Iconic example: The block_content module creates a block plugin for each Block Content entity. These entities, similar to nodes, are designed to be reused in page regions (via the block display interface).
→ Advantage: You can add a "FAQ" block to all your product pages without writing a single line of code.
The problem: A ticking time bomb for your site
Drupal's plugin system is not optimized to handle a large number of definitions. Here's why derivatives become dangerous:
1. Cache explosion (cache_discovery)
- Each plugin type has a single cache entry, containing all its definitions.
- Example: If you have 10,000 Block Content entities, their cache weighs several dozens of MB (I've seen entries over 40 MB+!).
- Worse: Every addition/modification of a Block Content (e.g., a "Hero" or "FAQ" block in a recipe) invalidates the entire cache entry. Drupal must then reload all definitions to rebuild it.
2. No database filtering
- When Drupal loads a plugin, it must load all definitions from the cache.
- Filtering is done only in PHP, after loading. It's impossible to pre-select the necessary plugins.
3. No hook to modify definitions before discovery
- Hooks like hook_plugin_filter_TYPE_alter or hook_plugin_filter_TYPE__CONSUMER_alter act too late: after caching or loading.
- Consequence: You cannot limit the number of generated definitions.
Why isn't your site crashing (yet)?
The block_content module intelligently filters blocks:
- Only reusable blocks (standard blocks) are returned as derivatives in the getDerivativeDefinitions() method.
- Blocks created via Layout Builder (non-reusable) are excluded.
Here's how it's done in the block_content deriver class:
public function getDerivativeDefinitions($base_plugin_definition) {
$block_contents = $this->blockContentStorage->loadByProperties(['reusable' => TRUE]);
$this->derivatives = [];
foreach ($block_contents as $block_content) {
$this->derivatives[$block_content->uuid()] = $base_plugin_definition;
$this->derivatives[$block_content->uuid()]['admin_label'] = $block_content->label() ?? ($block_content->type->entity->label() . ': ' . $block_content->id());
$this->derivatives[$block_content->uuid()]['config_dependencies']['content'] = [
$block_content->getConfigDependencyName(),
];
}
return parent::getDerivativeDefinitions($base_plugin_definition);
}An issue is ongoing for inline entity form regarding this, but reusable = FALSE also implies considering the entity as an "inline entity," which, from the core's perspective, also requires implementing the inline entity API: https://www.drupal.org/project/inline_entity_form/issues/3002175.
No miracle solution in sight
IOSAN contributed to a study, and a proposed fix was discussed with core contributors, but the side effects on the plugin system were too risky.
Conclusion: It's up to contrib modules to handle this issue.
Recommendations: How to avoid disaster?
For Site Builders
Use modules for what they are designed for:
- Never create an inline entity reference field that generates Block Content.
- Prefer dedicated solutions (e.g., Paragraphs or Layout Builder modules with clear limits).
For module developers
- Avoid derivatives based on content or configurations without a whitelist:
→ Example: Do not generate dynamic plugins from user entities or configurations. - Avoid cascading derivations (e.g., derivative from a field > a bundle > a view mode).
→ Risk: Exponential growth in the number of definitions (impossible to predict). - Document the risks: Warn users in the documentation.
- Implement hook_requirements():
→ Example: Display a warning if the number of derivatives becomes critical. - Favor configuration over definition:
- Design your plugins as "types" (e.g., field_type).
- The plugin itself should load its data based on its configuration, not its definition.
→ Advantages: Better performance, more open and scalable system.
In summary: Should Derivative Plugins be banned?
No, but use them with extreme caution:
✔ OK for controlled cases (e.g., a few reusable blocks, derivation from a single level of config entity).
❌ NEVER for massive or uncontrolled generation (e.g., based on third-party configs, cascades, inline entity reference fields).
Start on the right path (or get back on track)
Whether your project is in the planning stage or already in production, IOSAN helps you stay on track to ensure your project remains performant, scalable, and secure in the long run.
We can support you upfront in defining the content architecture and making technical choices.
We can also conduct a targeted audit and propose effective solutions tailored to your context.