Skip to main content

Anti-Patterns

Common mistakes developers make when working with the Magento_Customer module, why they're problematic, and correct implementations.

Best Practices 6 Categories

Table of Contents

Data Access Anti-Patterns

Anti-Pattern 1: Direct Model Usage Instead of Repository

Why It's Bad

  • Bypasses Service Contracts: Extensions won't intercept this operation
  • Skips Plugins: Plugins on CustomerRepositoryInterface won't execute
  • No Event Dispatch: customer_save_after_data_object event not fired
  • Breaks API Compatibility: Third-party code expecting repository pattern won't work
Bad Code Example Bad
<?php
namespace Vendor\Module\Model;

use Magento\Customer\Model\CustomerFactory;

class CustomerService
{
    private CustomerFactory $customerFactory;

    public function updateCustomerName(int $customerId, string $firstname, string $lastname): void
    {
        // WRONG: Direct model manipulation
        $customer = $this->customerFactory->create();
        $customer->load($customerId);
        $customer->setFirstname($firstname);
        $customer->setLastname($lastname);
        $customer->save();
    }
}
Good Code Example Good
<?php
declare(strict_types=1);

namespace Vendor\Module\Model;

use Magento\Customer\Api\CustomerRepositoryInterface;

class CustomerService
{
    private CustomerRepositoryInterface $customerRepository;

    public function updateCustomerName(int $customerId, string $firstname, string $lastname): void
    {
        // CORRECT: Use repository service contract
        $customer = $this->customerRepository->getById($customerId);
        $customer->setFirstname($firstname);
        $customer->setLastname($lastname);
        $this->customerRepository->save($customer);
    }
}

Why It's Better

  • Service Contract Compliance: Follows Magento's service contract pattern
  • Plugin Support: All plugins execute correctly
  • Transaction Safety: Ensures atomic save operations
  • Event Dispatch: Proper event chain fires

Anti-Pattern 2: Loading Full Customer When Only ID Needed

Why It's Bad

  • Performance Waste: Loads all EAV attributes (10+ SQL queries) when only 1 field needed
  • Memory Overhead: Loads entire customer object into memory
  • Slow Response: 100-500ms customer load vs. 5-10ms direct query

Correct Approach: Direct SQL Query

Performance Gain: 10-50x faster

Use ResourceConnection for single-field lookups instead of loading full customer entity.

Anti-Pattern 3: Excessive Custom EAV Attributes

Why It's Bad

  • Query Complexity: Each EAV attribute = 1 LEFT JOIN (50 attributes = 50 JOINs)
  • Slow Customer Load: 2000ms+ load time with 50+ custom attributes
  • Database Stress: Complex execution plans, potential table locks

Correct Approach: Extension Attributes + Custom Table

Performance Gain: 10-100x faster

  • Single JOIN: Only 1 JOIN to extended data table
  • Fast Queries: 20-50ms vs. 2000ms
  • Lazy Loading: Extended data only loaded when needed

Cache Anti-Patterns

Anti-Pattern 4: Ignoring Customer Group Cache After Group Change

Why It's Bad

  • Stale Prices: Full Page Cache serves old pricing based on previous customer group
  • Wrong Promotions: Catalog rules don't apply immediately
  • Customer Confusion: "Why are prices still the same?"
  • Revenue Impact: Potential overcharging or undercharging

Why It's Better

  • Fresh Pricing: FPC cleared, next page load shows correct prices
  • Correct Business Logic: Group-based rules apply immediately
  • Data Integrity: Cache state matches database state

Observer Anti-Patterns

Anti-Pattern 6: Synchronous External API Calls in Observers

Why It's Bad

  • Slow Saves: Customer save operations take 200-1000ms longer
  • Failure Coupling: External API failure causes customer save to fail
  • Poor UX: Admin users wait for external API during customer edits

Performance Gain

10x faster: 51ms (with queue) vs. 500ms (synchronous API call)

Use async queue pattern for external API calls instead of blocking observer execution.

Anti-Pattern 7: Bulk Operations Not Disabling Observers

Why It's Bad

  • Extremely Slow: 10,000 saves × 50ms = 500 seconds (8+ minutes)
  • Unnecessary Work: Email sync, audit logs not needed for bulk operations
  • Timeout Risk: Script may hit max_execution_time

Performance Gain

1000x faster: 0.5 seconds vs. 500 seconds for 10,000 customers

Use direct SQL UPDATE for bulk operations instead of looping through repository saves.

Quick Reference Summary

Anti-Pattern Impact Correct Approach
Direct model usage Bypasses plugins, events Use repository interface
Loading full customer for one field 10-50x slower Direct SQL query for field
Excessive EAV attributes 2000ms+ load time Extension attributes + custom table
Sync external API in observer 10-100x slower saves Async queue pattern
Bulk ops with observers 25x slower Disable observers or direct SQL