I was pulled into a code review for an apparel brand whose checkout crashed immediately after updating from PrestaShop 8.1.1 to 8.1.4. The update itself was routine, taking less than ten minutes in staging, but customers suddenly could not complete purchases whenever a voucher code was applied. The culprit was not a core bug. It was a five-year-old override in /override/classes/CartRule.php that had quietly replaced a critical validation method. When core introduced an updated argument to fix a security edge case, that legacy override choked, threw a fatal type error, and killed the sales funnel.
Deciding between overriding vs extending in PrestaShop is one of the most critical architectural decisions you will make for your store. It separates stores that upgrade cleanly in an afternoon from stores that remain trapped on obsolete versions because their business logic is hopelessly entangled with core classes.
How the PrestaShop Override System Works (and Why It Rots)
The PrestaShop override system was designed decades ago as a quick mechanism to alter core behavior without directly hacking core files. When you drop a class into /override/classes/ or a controller into /override/controllers/, PrestaShop compiles a dynamic class index mapping. Your custom class (for example, Product extends ProductCore) intercepts execution whenever the system instantiates that object.
On paper, this sounds safe. In production, it creates severe architectural fragility. Here is why core overrides degrade over time:
- Method Signature Drift: When a new PrestaShop minor release modifies a core method signature—adding a typed argument, changing a return type, or altering default parameters—your override still enforces the old signature. PHP 8+ will throw fatal errors instantly.
- Module Collisions: PrestaShop attempts to merge module overrides automatically when you install third-party addons. If Module A and Module B both attempt to override
Order::add(), PrestaShop tries a rudimentary string-injection merge that frequently fails, corrupts the file, or silently discards critical logic. - Invisible Debt: Overrides do not advertise their presence. When a new developer joins your project or when you troubleshoot a checkout glitch, overrides sit outside standard module lifecycles, often escaping code reviews until production breaks.
Here is a subtle detail that many developers miss: PrestaShop’s cache/class_index.php file caches overrides aggressively. During development or module installation, if this cache fails to regenerate cleanly due to filesystem permissions or background job race conditions, your store can run a ghost state where an override is partially active for web requests but completely ignored by CLI cron jobs. I have wasted hours tracing discrepancies that boiled down to that single index file.
Extending PrestaShop: Hooks, Event Dispatchers, and Services
Extending PrestaShop means adding or altering functionality through official, decoupled extension points rather than intercepting raw class inheritance. In modern PrestaShop (1.7.8 through 8.x and beyond), this is achieved using three main mechanisms: native hooks, Symfony service decoration, and the Command Query Responsibility Segregation (CQRS) event pipeline.
Instead of hacking the Cart class to apply a custom fee, you attach a custom module to actionCartSave or actionCartUpdateQuantityBefore. Instead of rewriting the customer registration controller, you hook into actionCustomerAccountAdd or wrap the registration form with custom form handlers.
When you rely on proper custom PrestaShop module development, your code interacts only with declared interfaces and payloads. If core updates internal mechanics inside CartCore::update(), your hooked listener continues executing as long as the hook contract remains intact—which the PrestaShop core team maintains with strict semantic versioning.
The Power of Symfony Service Decoration
As PrestaShop migrated its back office and core domains to Symfony, it introduced a far superior pattern: Service Decoration. If you need to change how an administrative invoice generates or how product stock commands validate, you do not override the class. You decorate the underlying Symfony service inside your module’s config/services.yml.
Service decoration allows you to wrap the original core service inside your own implementation. You take the core output, modify it, and return the modified result without copying and pasting hundreds of lines of core code into a static override file. If PrestaShop updates the internal dependencies of that service during an upgrade, your decorator automatically receives the updated core service without requiring manual code refactoring.
Overriding vs Extending: The Maintenance and Upgrade Reality
The choice between these two approaches directly dictates your total cost of ownership. The following breakdown shows how both approaches perform in production environments:
- Upgrade Friction: Core overrides require manual line-by-line diffing against new release tags during every minor upgrade. Extending via hooks or decorated services allows automated upgrades to proceed with minimal regression testing.
- Isolation: Overrides live in the global scope of the store. If an override has a bug, the entire class fails for all features. Extensions live inside isolated modules; disabling the module instantly restores default store behavior.
- Testing & CI/CD: Testing an override in isolation is notoriously difficult because it depends on the runtime compilation of
class_index.php. Modules leveraging modern hooks and services can be unit-tested cleanly with PHPUnit and mocked dependencies. - Conflict Resolution: When two modules try to override the same method, human intervention is required to manually merge the files. When two modules hook into the same action, PrestaShop simply runs them sequentially based on standard hook positioning.
I routinely audit stores where owners complain that upgrades are “impossible” or that their agencies quoted tens of thousands of dollars just to move from 1.7.7 to 8.x. In almost every case, the technical debt is not the database schema or theme templates—it is an accumulation of 15 to 25 core overrides written by past developers taking shortcuts.
When Core Overrides Are Unavoidable (and How to Protect Yourself)
Is overriding ever justified? Yes. PrestaShop is still an architectural hybrid. While the back office and modern domains run on Symfony, significant portions of the front office and legacy core (such as complex cart calculations, multi-shipping logic, or specific payment gateways) lack comprehensive action hooks.
If you genuinely must override a core class because no hook exists, you must follow strict discipline to protect your future sanity:
- Never Copy Entire Methods Unnecessarily: If you only need to run code after a parent method finishes, call
parent::methodName()inside your override instead of duplicating 80 lines of core code. Duplicating core code is a guarantee of technical debt. - Document the Exact PrestaShop Version: Add an inline comment at the top of the override stating the exact PrestaShop version (e.g.,
// Implemented against PrestaShop 8.1.2) along with the specific business reason for the override. - Track Overrides in Git Independently: Ensure your
/override/directory is tracked strictly in version control. Never allow third-party modules to write to/override/automatically on production servers without checking the resulting diff into your deployment pipeline. - Contribute a Hook Upstream: If you find a place where an override was the only option, submit an issue or pull request to the PrestaShop GitHub repository requesting a hook. The core team actively adds new extension points to reduce override reliance across the ecosystem.
The Verdict: What Is Safer Long-Term?
Extending via modules, hooks, and decorated services is objectively safer, more maintainable, and significantly cheaper to support over the multi-year lifespan of an e-commerce store. Overrides should be treated as an architectural last resort—an emergency lever pulled only when no hook exists and the business requirement is non-negotiable.
Treat every override in your codebase as an active liability with an upcoming expiration date. Whenever a core update introduces a new hook that covers your custom use case, migrate away from your override immediately.
Having delivered more than 200 custom PrestaShop projects over the past 10+ years, I have seen firsthand how fragile shortcuts can cripple a thriving store. If you are struggling with upgrade errors, broken modules, or legacy overrides, you can get expert help to clean up your architecture and build a stable platform for growth.
Frequently Asked Questions
Can two different PrestaShop modules override the same core method?
PrestaShop attempts to merge multiple module overrides into a single file during installation, but this automated process frequently produces fatal syntax errors or overwrites previous logic. When two modules target the same method, a developer must manually merge the code into /override/ to avoid breaking store operations.
Does disabling overrides in the PrestaShop back office improve site performance?
Disabling overrides under Advanced Parameters > Performance slightly reduces class autoloading overhead, but the performance gain is minimal compared to caching or database optimization. Its primary value is diagnostic, allowing you to instantly determine whether a store malfunction is caused by custom override code or native core files.
How do I know if an override was broken by a PrestaShop update?
Compare the overridden method in your /override/ folder directly against the updated method in the core release on GitHub or in your /classes/ folder. Look specifically for changes in method visibility, return type declarations, parameter additions, and modified internal variable names that your override might be calling.