Skip to content
advanced Phase 111 · Migration

Strangler Fig Pattern in Magento 2

Implementing the strangler fig pattern for incremental migration including traffic routing and old system retirement

45m
2 problems
Topic Progress 0%

Strangler Fig Concept

Named After Nature

The strangler fig tree grows around a host tree, gradually replacing it. In software, we build new systems alongside legacy ones, routing traffic incrementally until the old system can be removed.

Magento 2 Application

Identify Strangling Points

// Legacy monolithic service
class LegacyOrderService
{
    public function createOrder($data) { /* 500 lines */ }
    public function processPayment($data) { /* 300 lines */ }
    public function sendConfirmation($data) { /* 100 lines */ }
    public function updateInventory($data) { /* 200 lines */ }
}

// Strangling points:
// 1. Order creation → New OrderService
// 2. Payment processing → New PaymentService
// 3. Notifications → New NotificationService
// 4. Inventory → New InventoryService

The Four Phases

1. Identify seams (boundaries between concerns)
2. Build new alongside old
3. Route traffic gradually
4. Remove old when fully replaced

Timeline example:
Month 1-2:  Identify seams, design new services
Month 3-4:  Build OrderService, route 10% traffic
Month 5-6:  Build PaymentService, route 25% traffic
Month 7-8:  Build remaining services, route 50% traffic
Month 9-10: Route 100% traffic, remove legacy

Benefits for Magento

1. No big-bang rewrite risk
2. Continuous delivery of value
3. Easy rollback at each step
4. Team learns incrementally
5. Business operations continue
6. Costs spread over time

Traffic Routing

Routing Strategies

Plugin-Based Routing

<!-- di.xml - Route to new service gradually -->
<config>
    <type name="Magento\Sales\Model\Order">
        <plugin name="route_to_new_order_service"
                type="Vendor\Migration\Plugin\OrderRoutingPlugin"
                sortOrder="10"/>
    </type>
</config>
// Plugin implementation
namespace Vendor\Migration\Plugin;

class OrderRoutingPlugin
{
    private int $rolloutPercentage;
    
    public function __construct(
        private LegacyOrderService $legacyService,
        private NewOrderService $newService,
        private ConfigInterface $config
    ) {
        $this->rolloutPercentage = $this->config->get('migration/order_rollout_percentage');
    }
    
    public function beforeCreateOrder(
        \Magento\Sales\Model\Order $subject,
        array $data
    ): array {
        // Route based on percentage
        if (rand(1, 100) <= $this->rolloutPercentage) {
            // Use new service
            $result = $this->newService->createOrder($data);
            // Log for monitoring
            $this->logger->info('New service used for order');
            return [$data];
        }
        
        // Fall back to legacy
        $this->logger->info('Legacy service used for order');
        return [$data];
    }
}

Feature Flag Routing

// Feature flag based routing
public function createOrder(array $data): OrderInterface
{
    if ($this->featureFlag isEnabled('new_order_flow')) {
        return $this->newOrderService->create($data);
    }
    
    return $this->legacyOrderService->create($data);
}

User-Based Routing

// Route specific customers to new system
public function createOrder(array $data): OrderInterface
{
    $customerId = $data['customer_id'] ?? null;
    
    // Beta customers first
    if (in_array($customerId, $this->betaCustomerIds)) {
        return $this->newOrderService->create($data);
    }
    
    // Customer segments
    if ($this->isHighValueCustomer($customerId)) {
        return $this->newOrderService->create($data);
    }
    
    return $this->legacyOrderService->create($data);
}

Geographic Routing

// Route by region
public function createOrder(array $data): OrderInterface
{
    $region = $data['shipping_address']['region_code'];
    
    if (in_array($region, ['US', 'CA', 'UK'])) {
        // New regions use new system
        return $this->newOrderService->create($data);
    }
    
    // Other regions use legacy
    return $this->legacyOrderService->create($data);
}

Monitoring Routing

// Track which system is being used
public function logRoutingDecision(string $service, array $context): void
{
    $this->statsd->increment("migration.routing.{$service}");
    
    $this->logger->info('Routing decision', [
        'service' => $service,
        'order_id' => $context['order_id'] ?? null,
        'customer_id' => $context['customer_id'] ?? null,
        'percentage' => $this->rolloutPercentage,
    ]);
}

Old System Retirement

Retirement Criteria

Readiness Checklist

Code:
- [ ] 100% traffic routed to new system
- [ ] No errors from legacy paths
- [ ] All tests passing without legacy code
- [ ] PHPStan/PSalm clean

Operations:
- [ ] Monitoring shows no legacy usage
- [ ] No support tickets related to legacy
- [ ] Team trained on new system
- [ ] Documentation updated

Business:
- [ ] Stakeholder sign-off
- [ ] No pending features on legacy
- [ ] Performance meets or exceeds legacy
- [ ] Cost savings documented

Phased Retirement

Phase 1: Disable legacy entry points
- Remove legacy routes
- Disable legacy API endpoints
- Keep code for reference

Phase 2: Remove dead code
- Delete unused legacy classes
- Remove legacy configuration
- Clean up database tables

Phase 3: Remove legacy infrastructure
- Remove legacy servers/containers
- Archive legacy logs
- Update architecture diagrams

Phase 4: Document learnings
- Write migration post-mortem
- Update runbooks
- Share lessons with team

Database Cleanup

-- After legacy code removed
-- Archive legacy tables
RENAME TABLE legacy_orders TO legacy_orders_archive;

-- Remove legacy columns
ALTER TABLE orders DROP COLUMN legacy_status;

-- Clean up legacy data
DELETE FROM legacy_data WHERE created_at < '2024-01-01';

Rollback After Retirement

If legacy is removed but issues found:
1. Re-enable legacy code from version control
2. Restore legacy database tables from backup
3. Route traffic back to legacy
4. Fix issues in new system
5. Re-attempt retirement

Max rollback window: 30 days after retirement

Success Metrics

Migration is successful when:
- 100% traffic on new system for 30+ days
- Error rate same or lower than legacy
- Performance same or better
- No critical issues for 2 weeks
- Team confident in new system
- Legacy code fully removed

Dual Running

Running Old and New Together

Data Synchronization

// Keep data in sync during migration
public function syncOrderData(int $orderId): void
{
    // Write to both systems
    $legacyOrder = $this->legacyOrderRepository->get($orderId);
    $newOrder = $this->convertToNewFormat($legacyOrder);
    
    $this->newOrderRepository->save($newOrder);
    
    // Log sync for monitoring
    $this->logger->info('Order synced', ['order_id' => $orderId]);
}

Consistency Checks

// Verify data consistency between systems
public function verifyOrderConsistency(int $orderId): bool
{
    $legacy = $this->legacyOrderRepository->get($orderId);
    $new = $this->newOrderRepository->get($orderId);
    
    $diffs = [];
    
    if ($legacy->getGrandTotal() !== $new->getGrandTotal()) {
        $diffs[] = 'total_mismatch';
    }
    
    if ($legacy->getStatus() !== $new->getStatus()) {
        $diffs[] = 'status_mismatch';
    }
    
    if (!empty($diffs)) {
        $this->alertService->send('Data inconsistency', $diffs);
        return false;
    }
    
    return true;
}

Cost of Dual Running

Infrastructure:
- Double server capacity
- Additional database storage
- Duplicate cache layers
- Additional monitoring

Operations:
- Team supports both systems
- More complex debugging
- Additional deployment steps
- Extended testing requirements

Minimize duration:
- Plan aggressive but safe timeline
- Automate synchronization
- Monitor closely
- Fix issues quickly

Transition Checklist

Daily during dual running:
- [ ] Verify data sync is working
- [ ] Check error rates on both systems
- [ ] Monitor performance metrics
- [ ] Review routing percentages
- [ ] Address any inconsistencies

Weekly:
- [ ] Review migration progress
- [ ] Adjust rollout percentage
- [ ] Update stakeholders
- [ ] Plan next phase

Monthly:
- [ ] Comprehensive comparison
- [ ] Cost analysis
- [ ] Timeline review
- [ ] Risk assessment update

Practice Problems

0 / 2 solved
Strangler Fig Implementation

Implement a strangler fig migration for the order processing module, including traffic routing and monitoring.

Legacy Retirement Plan

Create a plan to retire the legacy order processing system after successful migration.

Quiz

1. What is the first step in strangler fig migration?

Question 1 options

2. How is traffic routed between old and new systems?

Question 2 options

3. When can the old system be retired?

Question 3 options

4. What is dual running?

Question 4 options

Flashcards

Question

What is strangler fig pattern?

Answer

Incrementally replace legacy by building new alongside old, routing traffic gradually

Question

What are seams?

Answer

Boundaries between concerns where code can be split into separate services

Question

How to route traffic?

Answer

Using plugins, feature flags, user-based rules, or geographic routing

Question

When to retire old system?

Answer

After 100% traffic for 30+ days, no errors, performance meets baseline

Question

What is dual running cost?

Answer

Double infrastructure, additional operations overhead, extended testing requirements

Revision Notes

Key Takeaways

  • 1. Strangler fig = 4 phases: identify seams, build new, route traffic, remove old
  • 2. Use plugins and feature flags for gradual traffic routing
  • 3. Monitor routing decisions and data consistency during dual running
  • 4. Retirement criteria: 100% traffic 30+ days, no errors, stakeholder sign-off
  • 5. Minimize dual running duration to reduce costs
  • 6. Always have rollback plan even after retirement (30-day window)

Interview Tips

  • Explain the strangler fig pattern and why it's safer than big-bang rewrite
  • How do you route traffic between old and new systems?
  • What are the costs of dual running?
  • How do you know when to retire the old system?
  • Describe a data synchronization challenge during migration

Cheat Sheet

Strangler Fig Pattern Cheat Sheet

4 Phases:

  1. Identify seams (boundaries)
  2. Build new alongside old
  3. Route traffic gradually
  4. Remove old when fully replaced

Routing Methods:

  • Plugin-based (percentage)
  • Feature flags (boolean)
  • User-based (beta customers)
  • Geographic (by region)

Retirement Criteria:

  • 100% traffic 30+ days
  • No errors from legacy
  • Performance meets baseline
  • Stakeholder sign-off

Dual Running Costs:

  • 2x infrastructure
  • Additional operations
  • Extended testing