How to Build a Production-Grade Custom Magento 2 Module
A production Magento 2 module needs the right registration (registration.php, module.xml), declarative schema for DB changes, dependency injection over ObjectManager, service contracts for APIs, and tests. Skipping declarative schema or leaking ObjectManager are the two most common reasons modules break on upgrade.
A production-grade Magento 2 module needs correct registration, declarative schema for any database change, dependency injection instead of ObjectManager, service contracts for public APIs, plugins instead of class rewrites, and automated tests. The two failures that break modules on upgrade are skipping declarative schema and leaking ObjectManager.
Key takeaways
- Use declarative schema (db_schema.xml), not InstallSchema scripts — it survives upgrades cleanly.
- Inject dependencies via the constructor; never call
ObjectManagerdirectly in business code. - Expose functionality through service contracts (Api/ interfaces) so other modules depend on stable APIs.
- Prefer plugins (interceptors) over class rewrites to stay upgrade-safe.
- Ship unit + integration tests; Magento's test framework catches DI and schema regressions early.
1. Registration and declaration
Every module starts with registration.php and etc/module.xml (with an accurate setup_version-free declaration on Magento 2.3+). Declare module dependencies in module.xml so the dependency graph and load order are explicit.
2. Database changes: declarative schema only
Define tables and columns in etc/db_schema.xml and generate db_schema_whitelist.json. Declarative schema is reversible and idempotent across upgrades — the single biggest reason legacy modules break is old InstallSchema/UpgradeSchema scripts that no longer run.
3. Dependency injection over ObjectManager
Type-hint dependencies in your constructor and let Magento's DI container provide them. Direct ObjectManager::getInstance() calls hide dependencies, break testability, and are flagged by Magento coding standards. Reserve ObjectManager for factories and a few framework edge cases only.
4. Service contracts for anything public
If other modules (or your own frontend/API) consume your logic, expose it through interfaces under Api/ and Api/Data/, bound in di.xml. Service contracts give you a stable surface and make REST/GraphQL exposure trivial.
5. Extend behavior with plugins, not rewrites
Use before/after/around plugins to modify core behavior. Class preferences (rewrites) collide with other modules and silently lose changes on upgrade. Use around sparingly — it has the highest performance cost.
6. Test it
Add unit tests for pure logic and integration tests for DI wiring and schema. A module without tests is a module that breaks quietly during the next platform upgrade.
Need this done to a standard you can hand off? See Magento / Hyvä Engineering or request a quote.
Frequently asked questions
What is the difference between declarative schema and setup scripts in Magento 2?
Declarative schema (db_schema.xml) describes the desired database state and Magento computes the changes, so it is idempotent and upgrade-safe. Old InstallSchema/UpgradeSchema scripts run imperatively and frequently fail to execute on later versions, which breaks modules.
Should I use plugins or rewrites in Magento 2?
Use plugins (interceptors) in almost all cases. Class rewrites (preferences) conflict when two modules target the same class and can silently drop changes on upgrade. Reserve around-plugins for cases you truly need because they carry the most performance overhead.
Why is using ObjectManager directly discouraged in Magento 2?
Direct ObjectManager calls hide a class's real dependencies, make unit testing difficult, and violate Magento coding standards. Inject dependencies through the constructor instead and let the DI container resolve them.
Do Magento 2 modules need tests?
Yes for anything production. Unit tests cover business logic and integration tests verify dependency injection and schema. Tests are the cheapest way to catch regressions before a Magento platform upgrade breaks the module in production.
Wizutech Admin
Wizutech Engineering
// Next step
Ready for your
own case study?
Every log here started with one conversation. Tell us what you're running.