Claude Code Anti-Patterns to Avoid in Magento 2 Projects
10 anti-patterns Claude (and you) keep falling into
These are the bad shortcuts I catch in code review every week. Add them to your CLAUDE.md so the model stops shipping them.
Tip: paste the 10 rules at the top of your CLAUDE.md under a "## Magento anti-patterns: never do these" heading. Claude reads CLAUDE.md before every turn.
Keep going
- Learning paths Backend, Hyvä, DevOps, architect: pick the path that matches your role.
- Magento playground Safe sandbox recipes for plugins, observers, GraphQL, and UI components.
- 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.
- 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
Why is ObjectManager::getInstance in production code such a big deal?
It bypasses the entire DI container, which means: (1) plugins on the resolved class never fire, including security plugins from third-party modules; (2) the class can't be mocked for unit tests, so coverage drops; (3) preferences in etc/di.xml are ignored, so a customer who overrode the class gets the default instead. Adobe's EQP checklist auto-fails the PR. The only legal locations are Setup/Patch/ (no DI yet at install time), Test/Unit/ (mocking framework needs it), and Block/ if you literally have no choice (you do have a choice, use a ViewModel). Replace every call with constructor injection.
Claude suggested editing vendor/magento, why is that a hard no?
Three reasons. (1) Composer wipes vendor/ on every composer install, your edit vanishes silently in CI. (2) Adobe's security patches replace the file on the next composer update, taking your fix with it but also reintroducing whatever bug you fixed. (3) Marketplace tech-review hard-fails any module that ships diffs against vendor. The right pattern: copy the file's logic into a plugin (around if you need to short-circuit, after for mutation), or a preference if you really must replace. Add a CI guard: git diff --name-only origin/main HEAD | grep '^vendor/' && exit 1.
What's wrong with using preferences for everything?
Preferences don't compose. Two modules that both preference the same class can't coexist, whichever loads later wins, and the loser's logic vanishes silently. In a Magento store with 30+ third-party modules, this is roulette. Plugins compose: ten modules can around-plug the same method and they all run, ordered by sortOrder. The fix: 90% of the time you want a plugin, not a preference. Reserve preferences for: replacing a non-public method, overriding a final class, or changing constructor signature. And even then, prefer a virtualType + plugin first.
Claude wrote 200 lines of code on the first shot, should I trust it?
No, and the warning sign is exactly that, large, clean-looking output with no error feedback. Without an eval loop (php -l, phpcs, phpstan, MFTF) running automatically, the model is operating blind. Common silent failures in Magento: (a) typed against an interface that doesn't exist in your version (e.g. used Magento\\Customer\\Api\\Data\\AddressInterface when the calling code wanted QuoteAddressInterface); (b) wired plugin in etc/di.xml instead of etc/frontend/di.xml; (c) used GraphQL @resolver syntax that compiled, but the schema cache still serves the old AST. Always run setup:upgrade and reload the affected page.
My Claude Code session keeps making the same mistake, how do I fix it?
This is context poisoning: a wrong assumption ended up in the conversation history early and now every reply reinforces it. Three fixes, in order: (1) /clear and start fresh with the correct constraint in your first message; (2) move the constraint into CLAUDE.md so it survives /clear; (3) if it's a class-of-mistake (e.g. "keeps using ObjectManager"), add a PostToolUse hook that greps for the pattern and feeds the failure back. Don't try to argue the model out of the mistake in-thread, you'll just spend tokens. Reset, anchor harder, and the next session lands clean.
Send a brief. Get a written quote in 24 hours.
Two paragraphs is enough: scope, price and timeline come back in writing.