---
name: magento2-dev-core
description: |
This skill should be used when the user is creating new Magento 2 modules or customizations,
implementing features following Magento architecture, working with Dependency Injection,
Repositories, or Plugins, writing secure Magento code, or building backend logic, CLI
commands, or cron jobs. Foundation skill for professional Magento 2 development. This is the
CORE skill that other Magento 2 skills depend on. Always load this first.
compatibility: claude, codex, opencode, copilot
metadata:
audience: developers
workflow: magento
requires: [magento2-linter, magento2-performance-audit]
---
# Magento 2 Developer Core
This skill provides the foundational patterns all Magento 2 developers must follow. It covers architectural decisions, security, and best practices that apply to every part of a Magento project.
## Related Skills
This is the foundation the other Magento 2 skills build on: `magento2-frontend-dev` and `magento2-hyva-dev` cover the two mutually exclusive theme stacks (check the theme's `theme.xml` parent to see which one the project actually uses — Luma vs Hyvä), `magento2-backend-dev` covers APIs/CLI/cron, and `magento2-linter`, `magento2-security-scan`, `magento2-performance-audit` verify the patterns below — `magento2-code-review` orchestrates all three (plus this skill's own anti-pattern checks) into one report scoped to a PR/module/theme/project. In a Govard environment, pair this with `govard-magento` for the container/CLI side.
## Core Architectural Standards
### Dependency Injection (DI)
**DO**: Use Constructor Injection for all dependencies.
```php
class MyService
{
public function __construct(
private readonly ProductRepositoryInterface $productRepository,
private readonly LoggerInterface $logger
) {}
public function getProduct(int $id): ?ProductInterface
{
return $this->productRepository->get($id);
}
}
```
**NEVER**: Use `ObjectManager::getInstance()` (Service Locator anti-pattern).
```php
// WRONG - Never do this
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$product = $objectManager->create(Product::class);
// CORRECT
public function __construct(ProductFactory $productFactory) {
$this->productFactory = $productFactory;
}
```
### Service Contracts
Always prefer interfaces in `Api/` folders over concrete classes:
```php
// WRONG
public function __construct(Product $product) { }
// CORRECT
public function __construct(ProductInterface $product) { }
```
### Repositories
Always use repositories for data operations. Never call `load()`, `save()`, or `delete()` directly on models.
```php
// WRONG
$product = $this->productFactory->create();
$product->load($id);
// CORRECT
$product = $this->productRepository->getById($id);
// WRONG
$this->productFactory->create()->save($product);
// CORRECT
$this->productRepository->save($product);
```
### Plugins (Interceptors)
Prefer `before` and `after` plugins over `around` plugins:
| Plugin Type | Use Case |
|-------------|----------|
| `before` | Modify arguments before method execution |
| `after` | Modify return value after method execution |
| `around` | **Avoid unless necessary** - blocks original method execution |
```php
// Prefer this pattern
public function beforeExecute(
SaveProduct $subject,
ProductInterface $product
): array {
// Validate or modify $product before save
return [$product];
}
// Instead of around plugins that wrap the entire method
```
Plugins only intercept **public** methods, must be stateless, and should not target a module's own classes or data objects. Register them in `di.xml` with an explicit `sortOrder` when order matters. Observer order is *not* guaranteed by contrast — if the sequence matters, use a plugin instead of an observer. Before adding a new plugin or observer, check for existing ones on the same target class/event — `magento2-code-review`'s plugin/observer conflict check (`references/plugin-observer-conflict-check.md` in that skill) covers the grep procedure.
### Declarative Schema
Use `db_schema.xml` for all database changes. Never modify database directly:
```xml