From scaffolding to a finished plugin
Claude can take a lot of routine work off your hands in Magento 2 module development: setting up folder structures, generating plugin and observer skeletons, and explaining unfamiliar core classes. Giving it the right DI conventions and service contract patterns as context produces noticeably more usable suggestions than generic PHP requests.
Table of Contents
- 1. Where Claude actually helps in Magento module development
- 2. Module scaffolding: folder structure and required files
- 3. Generating plugin skeletons
- 4. Observer skeletons and event context
- 5. Providing DI conventions as context
- 6. Understanding and generating service contracts
- 7. Explaining unfamiliar core classes
- 8. Practical example: building a small custom module
- 9. Prompt quality in direct comparison
- 10. Summary
- 11. FAQ
1. Where Claude actually helps in Magento module development
A large part of Magento 2 module development consists of recurring structure: registration.php, module.xml, di.xml, interfaces, repositories, and the matching XML configuration for ACL, system configuration, and menu entries. This share of boilerplate is exactly where Claude works most reliably, because these patterns are well documented and appear thousands of times in the official Magento codebase. A developer saves time here without handing off any domain decisions to the model.
Things get harder with architectural decisions: whether a plugin or an observer is the right choice, whether a new service contract is needed or an existing one should be extended, depends on the concrete context of the shop, which Claude does not automatically know. Here the model only delivers usable suggestions if the developer actively supplies that context, for instance which core class is affected and what behavior needs to change. The following sections show concrete usage patterns: from scaffolding through plugin and observer skeletons to a complete example module.
2. Module scaffolding: folder structure and required files
A new Magento module always needs the same basic files, which makes scaffolding a good first task for Claude Code. Instead of dictating every file individually, you describe the module name, purpose, and planned functional areas, and Claude creates registration.php, etc/module.xml, composer.json, and the base folder structure following Magento convention. It is important to explicitly state the target version (Magento 2.4.8, PHP 8.4) and the vendor namespace in the prompt, otherwise the model falls back on outdated patterns from older Magento versions that appear more frequently in training data.
On projects with a CLAUDE.md file in the repository root that documents naming conventions, path structure, and coding standards, this step turns out even more precise, because Claude reads and respects that file automatically. In practice it pays off to verify the generated scaffold immediately with bin/magento module:status and bin/magento setup:upgrade instead of blindly trusting completeness. If, for example, the sequence entry for a dependency is missing, that shows up right away during the upgrade.
# Ask Claude Code to scaffold a new module, then verify immediately
bin/magento module:status | grep Mironsoft_ProductBadges
# After scaffolding, always run setup:upgrade to catch missing
# sequence entries or malformed module.xml before writing logic
bin/magento setup:upgrade
bin/magento setup:di:compile
# Verify the module is registered and enabled
bin/magento module:status Mironsoft_ProductBadges
3. Generating plugin skeletons
Plugins are the standard tool in Magento 2 for extending the behavior of core or third-party module classes without touching the original code. Claude generates usable plugin skeletons reliably when you name three things explicitly: the fully qualified target class, the method to be extended, and whether before, after, or around is needed. Without this precision the model tends to automatically suggest an around plugin, even though a simpler after plugin would suffice and carries less risk of interfering with other plugins in the call chain.
A common mistake in AI-generated plugins is an incorrectly typed $subject argument or a missing sortOrder declaration in di.xml when several plugins act on the same method. Claude knows the Magento convention that afterX and beforeX are named after the method x(), but it only generates these naming patterns reliably and correctly when the exact method name is given in the prompt. A quick review of the generated di.xml for typos in the class path is mandatory, since Magento does not throw an error at compile time for a wrong path, it simply lets the plugin silently fail to take effect.
<?php
declare(strict_types=1);
namespace Mironsoft\ProductBadges\Plugin\Catalog\Model;
use Magento\Catalog\Model\Product;
/**
* Adds a computed badge label to product data after core price calculation.
*/
class AddBadgeLabelPlugin
{
/**
* Appends a "new" badge flag based on the product's creation date.
*
* @param Product $subject Original product model instance.
* @param Product $result Result returned by the original getFinalPrice call.
* @return Product Modified product instance with badge data set.
*/
public function afterGetFinalPrice(Product $subject, Product $result): Product
{
$createdAt = strtotime((string) $subject->getCreatedAt());
if ($createdAt !== false && $createdAt > strtotime('-14 days')) {
$subject->setData('badge_label', 'new');
}
return $result;
}
}
4. Observer skeletons and event context
Observers react to events that Magento dispatches at defined points in the code, for example catalog_product_save_after or sales_order_place_after. The decisive difference from plugins is that observers cannot influence a return value and should be thought of as asynchronous, even though Magento executes them synchronously by default. Claude reliably generates correct observer classes when the event name and the data available in the event object are stated in the prompt, because this information is not part of the interface, it only lives in the respective dispatch calls in the core.
A recurring problem: Claude sometimes suggests running heavy operations, such as external API calls, directly inside the observer, which noticeably slows down the admin interface for synchronous events like catalog_product_save_after. The better hint in the prompt is to explicitly ask for a message queue connection instead of a direct observer call for such cases. Claude reliably generates the events.xml registration, including scope (global, adminhtml, frontend), correctly when the desired area is clearly named.
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
<!-- Fires after a product entity is persisted -->
<event name="catalog_product_save_after">
<observer name="mironsoft_productbadges_recalculate_badge"
instance="Mironsoft\ProductBadges\Observer\RecalculateBadgeObserver"/>
</event>
</config>
5. Providing DI conventions as context
Magento's dependency injection container follows its own conventions that differ from generic Symfony or Laravel DI: virtualTypes, preferences, type configurations with argument arrays, and the distinction between global and area-specific di.xml. Claude generally knows these patterns from training data, but applies them more precisely when the prompt explicitly mentions that this is Magento DI and not a generic PHP DI framework. Without that hint, Symfony autowiring assumptions occasionally slip into the suggestions, and those simply do not work in Magento.
The context is especially valuable for virtualType definitions, which are frequently used in Magento to instantiate a class multiple times with different configuration, for example for different logger channels. Claude reliably generates correct virtualType blocks here when you state the base class pattern and the desired configuration difference. For constructor property promotion in PHP 8.4, the rule is: Claude uses this feature automatically when it is set as a project standard in the prompt or in CLAUDE.md, but without that hint it often still generates classic property declarations with a separate constructor body.
{
"note": "Example di.xml logic expressed as structured context for a prompt",
"target_class": "Mironsoft\\ProductBadges\\Model\\BadgeCalculator",
"interface": "Mironsoft\\ProductBadges\\Api\\BadgeCalculatorInterface",
"preference_scope": "global",
"virtual_type_needed": true,
"virtual_type_purpose": "separate logger channel for badge calculation errors",
"constructor_property_promotion": true,
"php_version": "8.4"
}
6. Understanding and generating service contracts
Service contracts are stable PHP interfaces in a module's Api directory that define the module's public API independently of the concrete implementation. Claude reliably generates clean service contract structures when you explicitly specify the pattern: interface in the Api folder, data object interface in the Api/Data folder, implementation in the Model folder with a preference binding in di.xml. Without this specification the model occasionally mixes interface and implementation into one file or mistakenly places the implementation directly in the Api folder.
A detail Claude often overlooks without an explicit hint: repository interfaces should accept a SearchCriteriaInterface for list queries and return a SearchResultsInterface, instead of inventing custom array-based filter methods. Referencing an existing Magento repository, such as ProductRepositoryInterface, as a reference pattern in the prompt gets noticeably more consistent results. It remains important that getter and setter methods in data object interfaces follow Magento's naming conventions exactly, since reflection-based mechanisms like the ObjectManager rely on them.
<?php
declare(strict_types=1);
namespace Mironsoft\ProductBadges\Api;
use Mironsoft\ProductBadges\Api\Data\BadgeInterface;
/**
* Public service contract for computing and persisting product badges.
*/
interface BadgeCalculatorInterface
{
/**
* Calculates the badge for a given product SKU.
*
* @param string $sku Product SKU to evaluate.
* @return BadgeInterface Computed badge data object.
* @throws \Magento\Framework\Exception\NoSuchEntityException
*/
public function calculateForSku(string $sku): BadgeInterface;
}
7. Explaining unfamiliar core classes
The Magento core comprises thousands of classes, and even experienced developers regularly run into unfamiliar territory, for example on first contact with the indexer framework, the EAV system, or the checkout LayoutProcessor chain. Claude is particularly strong here as an explanatory tool, because it does not just describe the method, it also places it in architectural context, for example why a class implements an interface or where it typically sits in the call graph. This does not replace Magento documentation, but it usefully complements it for a fast start.
Explanations are most reliable when the actual source code of the class is provided as context, instead of relying on the class name alone. Claude Code can read files directly and then cites concrete lines, which noticeably reduces hallucination compared to a pure name-based request without code. Caution is warranted with strongly generic core classes such as \Magento\Framework\Model\AbstractModel: Claude correctly knows many public methods, but it cannot know the actual control flow in a specific shop when project-specific extensions exist through preferences or plugins, unless those extensions are explicitly mentioned.
8. Practical example: building a small custom module
A realistic example illustrates the full workflow: a module should automatically assign a "new" badge to products created within the last 14 days and expose that badge through a service contract to the frontend template. The prompt to Claude Code describes purpose, vendor namespace, target version, and the desired building blocks: an Api interface, a Model implementation, a plugin on price calculation as the trigger, and the matching configuration files for ACL and system configuration according to the CLAUDE.md specification.
After generation comes the most important step, one that is often skipped: run bin/analyse with PHPStan at level 5, actually read the generated files instead of just skimming them, and manually check the ACL structure in the admin panel under System > Permissions. In this example, a first PHPStan run revealed that the generated SearchCriteria handling in the repository had the wrong return type, something that would only have surfaced at runtime without that analysis. This cycle of generating, checking, and targeted refinement is the actual productive core of the workflow, not the plain act of producing code.
9. Prompt quality in direct comparison
The quality of the suggestions depends almost entirely on how much Magento-specific context the prompt supplies. A generic PHP prompt frequently results in code that is syntactically correct but ignores Magento conventions, for example missing service contracts, wrong DI patterns, or direct ObjectManager calls instead of constructor injection. The table below shows typical prompt formulations compared.
| Task | Weak Prompt | Precise Prompt | Effect |
|---|---|---|---|
| Creating a new module | "Create a Magento module" | State vendor, module name, Magento version, purpose, and CLAUDE.md conventions | Correct namespace and folder structure without rework |
| Writing a plugin | "Extend the product class" | State the fully qualified target class, method, and before/after/around | Correct plugin type instead of a risky around default |
| Registering an observer | "React to product saving" | Give the exact event name and scope (global/adminhtml/frontend) | Correct events.xml without wrong scope |
| Understanding a core class | "What does AbstractModel do?" | Provide the actual source code and concrete method | Less hallucination, concrete line references |
| Service contract | "Create an interface for badges" | State the Api/Api-Data structure and reference an existing repository interface | Clean separation of interface and implementation |
Mironsoft
Magento and Hyva module development with an experienced team and modern tooling support
Planning a custom module for your Magento shop?
We build Magento 2 modules following service contract conventions, with clean dependency injection and full PHPStan coverage, and support teams in productively using Claude Code for module development.
Module Architecture
Service contracts, DI structure, and ACL configuration following Magento convention
Code Review
PHPStan level 5 checks and manual control of AI-generated modules
Team Enablement
Building CLAUDE.md conventions and Claude Code workflows for Magento teams
10. Summary
Claude is particularly well suited to Magento 2 module development for tasks with clear, well-documented patterns: folder structure, required files, plugin and observer skeletons, and explaining unfamiliar core classes based on the actual source code. The decisive factor for the quality of the results is the context you provide: Magento version, vendor namespace, exact target classes and methods, and project-specific conventions from a CLAUDE.md file lead to noticeably more precise code than generic PHP requests.
Architectural decisions, such as choosing between a plugin and an observer or designing a new service contract, remain the developer's responsibility even when Claude assists with implementation. The productive core of the workflow lies in the cycle of generating, checking with PHPStan and similar tools, and targeted refinement, not in blindly accepting generated files. Anyone who consistently follows this cycle gains noticeable time on routine tasks without losing control over the module architecture.
Using Claude for Magento Module Development: Key Takeaways
Scaffolding
Folder structure, registration.php, and module.xml generated reliably when Magento version and vendor namespace are clearly stated.
Plugins & Observers
Fully qualified target class, method, and event name in the prompt avoid wrong plugin types and wrong event scope.
DI & Service Contracts
Explicitly state "Magento DI" instead of generic PHP DI. Referencing existing core interfaces improves consistency.
Review Requirement
PHPStan level 5, manual ACL checks, and reading the generated code are a fixed part of the workflow, not an optional step.