Claude Code Playground: Magento 2 Sandbox Recipes
How I use Claude Code on Magento work day to day: the prompts, the guardrails, and the places it should not be trusted.
The in-page playground is being rebuilt. Send a brief and the answer comes back written, with the workings.
Keep going
- Learning paths Backend, Hyvä, DevOps, architect: pick the path that matches your role.
- Module anatomy Every Magento 2 module file, mapped and explained.
- Glossary 80+ Magento and Claude Code terms, defined without jargon.
- Recipes cookbook Copy-paste recipes that turn one prompt into shippable Magento code.
- Prompt engineering Write prompts that produce Marketplace-pass Magento code.
- Anti-patterns Twelve ways AI-paired Magento work goes wrong, and the fixes.
- CLI sandbox Run Claude Code on Magento safely with Docker, hooks, and allowlists.
- Slash-commands Production slash-commands for Magento 2 workflows.
- 30-day roadmap Day-by-day plan to ship a Magento module with Claude Code.
- Code reviewer agent A drop-in Claude sub-agent that reviews Magento PRs for shippability.
Common questions
What's the cleanest way to add a Plugin around Product::getPrice in Magento 2.4?
Wire it in etc/di.xml against Magento\Catalog\Pricing\Price\FinalPrice, not Product::getPrice. Catalog price uses the price-pool pattern since 2.2: Product::getPrice returns the base attribute value, but the storefront and cart call FinalPrice via the price-info object. Plugin signature: around aroundGetValue(FinalPrice $subject, callable $proceed). Always call $proceed() first, mutate the float, return it, never short-circuit, or you break tier prices and catalog rules. Sample di.xml:
Should I use a Plugin or a Preference to extend a Magento class?
Plugins compose; preferences hammer. A plugin is a stackable interceptor, multiple modules can plug the same method and they all run, ordered by sortOrder. A preference is a one-winner replacement, the last module loaded wins, and any other module that wanted to extend the same class loses silently. Rules of thumb: use around/before/after plugins for behaviour changes; use preference only when you need to replace a non-public method, override a final class, or change the constructor signature (and even then, prefer a virtualType + plugin first). Two preferences on the same class is a merge conflict waiting to happen.
Why does my UIComponent listing return 'No records found' even though I see rows in the DB?
Nine times out of ten the data-source is wired to the wrong collection, or the idFieldName in ui_component/ does not match the primary key on the SearchResult collection. Check three things: (1) etc/di.xml has a virtualType for with the right collection class; (2) the collection extends Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult, not the legacy resource collection; (3) component="Magento_Ui/js/grid/provider" is on the dataSource node. The grid silently fails open on schema mismatches, tail var/log/exception.log while you reload, the real error is there.
How do I add a custom GraphQL resolver without breaking the schema cache?
Three files, in this order. (1) etc/schema.graphqls, declare the type and the field; (2) a Resolver class implementing Magento\Framework\GraphQl\Query\ResolverInterface; (3) wire it in the schema with @resolver(class: "Vendor\\Module\\Resolver\\Foo"). After every schema edit run bin/magento cache:clean config graphql_query_resolver_cache. Don't skip config, the schema-stitcher caches the parsed AST there, not under graphql_query_resolver_cache. If your new field still 404s, run bin/magento setup:di:compile; resolver-class autoloading goes through generated factories.
Observer or plugin for "after order placed" logic?
Use a plugin on Magento\Sales\Api\OrderManagementInterface::place. The historic sales_order_place_after observer still fires, but it runs inside the same DB transaction as the order save, so any exception you throw rolls back the order, rarely what you want. A plugin gives you a clean afterPlace($subject, $result) hook where you can dispatch an async queue message, send an email, or fire a webhook without endangering the order itself. If you need the quote rather than the order, use Magento\Quote\Api\CartManagementInterface::placeOrder instead, it returns the order ID.
Send a brief. Get a written quote in 24 hours.
Two paragraphs is enough: scope, price and timeline come back in writing.