Shopware 6: plugins, not core hacks
An online shop is never really finished. Payment methods change, shipping providers rework their interfaces, and Shopware itself ships regular updates with security fixes and new features. That is exactly why the quality of a Shopware 6 project is not decided on launch day, but on the second, third and tenth update that follows.
Our principle for this is simple: we do not touch the Shopware core. Every customisation lives in a plugin, a theme or an app. Here is why that matters so much, and what it looks like in practice.
Why core changes get expensive
Editing a core file directly feels quick at first. The problem shows up later: the next update replaces that file, and the change has either silently vanished or has to be patched back in by hand. A year on, nobody remembers which lines were changed, or why.
The consequence is familiar. Updates get postponed because they feel risky. The shop stays on an old release, security fixes arrive late, and at some point the gap to the current version is so wide that an update amounts to a rebuild. A shop that can no longer be updated is a liability, both technically and commercially.
The tools Shopware already provides
Shopware 6 is built on Symfony and was designed for extension from the ground up. At its heart, a plugin is a Symfony bundle with a lifecycle of its own: it is installed, activated, updated and, if needed, removed again. Inside a plugin, several mechanisms are available.
Decorate services instead of replacing them
When a core feature needs to behave differently – price calculation, say, or the way products are loaded – we decorate the responsible service. The decorator pattern wraps the original service in a class of our own that implements the same interface and adds or changes only what is necessary. Everything else is passed on to the original. When Shopware improves the original service in an update, the shop benefits without any follow-up work on our side, as long as the interface stays stable.
React to events
Many customisations need no class changes at all. Shopware dispatches events at countless points: when a page is loaded, an order is placed or an entity is written. An event subscriber in the plugin hooks in exactly there, adds data for the template or triggers a follow-up process, such as handing an order over to an external system.
Inherit templates instead of copying them
In the storefront we rely on Shopware's Twig template inheritance. With sw_extends, a plugin or theme template extends the original and overrides individual blocks only. Calling parent() keeps the original content of a block. We change only the block that actually differs. The theme itself is a plugin too, and it is versioned and shipped the same way.
Model your own data properly
When a shop needs data structures of its own, we define them through Shopware's Data Abstraction Layer (DAL): an entity definition describes fields and associations, and a migration creates the table. The data then shows up in the APIs and in the administration automatically, and existing core entities can be extended without altering their tables.
Extend the administration
The administration is extensible as well. Custom modules, extra fields in existing forms or new settings pages are built as plugin components that Shopware includes during the build.
Plugin or app?
Alongside plugins, Shopware 6 offers the app system. An app does not run inside the shop; it talks to it through webhooks and the Admin API. That suits extensions for the cloud edition of Shopware, or logic that runs on a separate server anyway. Plugins, on the other hand, have full access to the Symfony world and are the right choice whenever you need to reach deep into the shop's processes. We make that call per requirement, not by default.
How we set up Shopware projects
- Composer-based project: Shopware and every dependency are pinned in
composer.json. Versions are traceable, and updates are applied deliberately. - Custom plugins in the repository: project-specific extensions are versioned alongside the project, with clear names and one job per plugin.
- No edits in the vendor directory: whatever lives there belongs to Shopware or to the makers of the extensions.
- Staging before production: updates go to a test environment with realistic data first and are checked there – checkout, payment methods, integrations.
- Automated checks: static analysis and tests for our own plugins catch broken interfaces early.
We set up new projects on Shopware 6.7. Because every customisation sits in a plugin of its own, we can check before each update which extension is affected by a changed interface.
The bottom line
Plugins, decorators, events and template inheritance are not extra effort – they are the way Shopware 6 was meant to be worked with from the start. If you want us to take over an existing shop that no longer survives updates, we begin by finding the places where the core was modified, and then move those changes into proper extensions, one step at a time.