Skip to content
advanced Phase 119 · Senior Projects

Project - Magento Version Upgrade

Plan and execute a major Magento version upgrade with zero downtime, data migration, and testing

3h
0 problems
Topic Progress 0%

Upgrade Planning and Assessment

Upgrade Assessment Matrix

Component            │ Current │ Target │ Risk   │ Effort
─────────────────────┼─────────┼────────┼────────┼────────
Magento Core         │ 2.4.5   │ 2.4.7  │ Medium │ High
PHP Version          │ 8.1     │ 8.2    │ Low    │ Medium
MySQL                │ 8.0     │ 8.0    │ None   │ None
Elasticsearch        │ 7.x     │ OpenSearch │ High  │ High
RabbitMQ            │ 3.x     │ 3.x    │ None   │ None
Redis                │ 6.x     │ 7.x    │ Low    │ Low
Node.js (for build)  │ 16      │ 20     │ Low    │ Low

Compatibility Check Script

#!/bin/bash
# check-compatibility.sh

echo "=== Magento Upgrade Compatibility Check ==="
echo ""

# Check current version
CURRENT_VERSION=$(bin/magento --version | grep -oP '\d+\.\d+\.\d+')
echo "Current Magento version: $CURRENT_VERSION"
echo ""

# Check PHP version
echo "PHP Version:"
php -v | head -1
echo ""

# Check extensions
echo "PHP Extensions:"
php -m | sort
echo ""

# Check custom modules
echo "Custom Modules:"
bin/magento module:status --enabled | grep -v 'Magento_' | grep -v 'List of'
echo ""

# Check composer dependencies
echo "Composer Dependencies:"
composer show --name-only | grep -v 'magento/' | head -20
echo ""

# Check deprecated code
echo "Deprecated Code Check:"
grep -r '@deprecated' app/code/ --include='*.php' | wc -l
echo " deprecated annotations found"
echo ""

# Check database schema
echo "Database Tables:"
mysql -u root -p -e "SHOW TABLES FROM magento2" | wc -l
echo " tables found"
echo ""

# Generate report
echo "=== Compatibility Report ==="
echo "Review the above output for potential upgrade issues."
echo ""
echo "Recommended actions:"
echo "1. Update PHP extensions if needed"
echo "2. Check custom module compatibility"
echo "3. Review deprecated code"
echo "4. Test in staging environment"

Upgrade Timeline

Phase 1: Assessment (Week 1-2)
  ├── Compatibility check
  ├── Custom module audit
  ├── Extension compatibility
  ├── Database schema review
  └── Risk assessment

Phase 2: Preparation (Week 3-4)
  ├── Set up staging environment
  ├── Clone production data
  ├── Update composer.json
  ├── Create backup procedures
  └── Write upgrade scripts

Phase 3: Development (Week 5-8)
  ├── Core upgrade
  ├── Custom module updates
  ├── Extension updates
  ├── Theme compatibility
  └── Database migration

Phase 4: Testing (Week 9-10)
  ├── Unit tests
  ├── Integration tests
  ├── Functional tests
  ├── Performance tests
  ├── Security tests
  └── UAT

Phase 5: Deployment (Week 11)
  ├── Blue-green deployment
  ├── Data migration
  ├── DNS cutover
  ├── Monitoring
  └── Rollback plan

Phase 6: Post-Upgrade (Week 12)
  ├── Monitor metrics
  ├── Fix issues
  ├── Optimize performance
  └── Documentation

Risk Assessment

risks:
  - name: Custom Module Incompatibility
    probability: High
    impact: High
    mitigation:
      - Audit all custom modules
      - Update deprecated code
      - Test each module individually

  - name: Data Loss During Migration
    probability: Low
    impact: Critical
    mitigation:
      - Full database backup
      - Test migration on staging
      - Verify data integrity

  - name: Performance Degradation
    probability: Medium
    impact: High
    mitigation:
      - Benchmark before upgrade
      - Test with production data volume
      - Optimize queries

  - name: Extension Conflicts
    probability: Medium
    impact: Medium
    mitigation:
      - Check extension compatibility
      - Update or replace extensions
      - Test thoroughly

  - name: Downtime During Deployment
    probability: Low
    impact: High
    mitigation:
      - Blue-green deployment
      - Rollback procedure
      - Maintenance window

Zero-Downtime Upgrade Strategy

Blue-Green Deployment

Blue-Green Deployment Flow:

1. Current (Blue) Environment:
   ┌─────────────────────────────────────┐
   │  Magento 2.4.5                       │
   │  ┌──────┐  ┌──────┐  ┌──────┐      │
   │  │ Web1 │  │ Web2 │  │ Web3 │      │
   │  └──────┘  └──────┘  └──────┘      │
   │         Load Balancer              │
   │              │                      │
   │         Database Primary            │
   └─────────────────────────────────────┘

2. Green Environment (Upgrade):
   ┌─────────────────────────────────────┐
   │  Magento 2.4.7                       │
   │  ┌──────┐  ┌──────┐                │
   │  │ Web1 │  │ Web2 │                │
   │  └──────┘  └──────┘                │
   │         Load Balancer (standby)     │
   │              │                      │
   │         Database Replica            │
   └─────────────────────────────────────┘

3. Cutover:
   - Promote Green to primary
   - Switch DNS/Load Balancer
   - Blue becomes standby
   - Monitor and rollback if needed

Upgrade Script

#!/bin/bash
# upgrade-magento.sh

set -e

# Configuration
CURRENT_VERSION="2.4.5"
TARGET_VERSION="2.4.7"
BACKUP_DIR="/var/backups/magento-upgrade"
LOG_FILE="/var/log/magento-upgrade.log"

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a $LOG_FILE
}

# Step 1: Enable maintenance mode
log "Enabling maintenance mode..."
bin/magento maintenance:enable

# Step 2: Backup
log "Creating backup..."
mkdir -p $BACKUP_DIR
bin/magento setup:backup --db --media --code

# Step 3: Backup database separately
log "Backing up database..."
mysqldump -u root -p magento2 > $BACKUP_DIR/db-$(date +%Y%m%d%H%M%S).sql

# Step 4: Put store in read-only mode
log "Setting database to read-only..."
mysql -u root -p -e "SET GLOBAL read_only = ON;"

# Step 5: Update composer.json
log "Updating composer.json..."
composer require \
    magento/product-community-edition=$TARGET_VERSION \
    --no-install

# Step 6: Install dependencies
log "Installing dependencies..."
composer update --no-dev --prefer-dist

# Step 7: Run setup upgrade
log "Running setup upgrade..."
bin/magento setup:upgrade --keep-generated

# Step 8: Compile code
log "Compiling code..."
bin/magento setup:di:compile

# Step 9: Deploy static content
log "Deploying static content..."
bin/magento setup:static-content:deploy -f

# Step 10: Clean cache
log "Cleaning cache..."
bin/magento cache:clean
bin/magento cache:flush

# Step 11: Reindex
log "Reindexing..."
bin/magento indexer:reindex

# Step 12: Disable read-only mode
log "Disabling read-only mode..."
mysql -u root -p -e "SET GLOBAL read_only = OFF;"

# Step 13: Disable maintenance mode
log "Disabling maintenance mode..."
bin/magento maintenance:disable

# Step 14: Verify
log "Verifying upgrade..."
bin/magento --version
bin/magento module:status

log "Upgrade complete!"

Database Migration

<?php
namespace Vendor\Upgrade\Setup\Patch\Data;

use Magento\Framework\Setup\Patch\DataPatchInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;

class MigrateCustomData implements DataPatchInterface
{
    public function __construct(
        private ModuleDataSetupInterface $setup,
    ) {
    }

    public function apply(): void
    {
        $this->setup->getConnection()->startSetup();

        // Example: Migrate custom table schema
        $connection = $this->setup->getConnection();
        $tableName = $this->setup->getTable('vendor_custom_table');

        // Add new column if not exists
        if (!$connection->tableColumnExists($tableName, 'new_field')) {
            $connection->addColumn(
                $tableName,
                'new_field',
                [
                    'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
                    'length' => 255,
                    'nullable' => true,
                    'comment' => 'New Field',
                ]
            );
        }

        // Migrate data
        $select = $connection->select()->from($tableName);
        foreach ($connection->fetchAll($select) as $row) {
            $newData = $this->transformData($row);
            $connection->update($tableName, $newData, ['entity_id = ?' => $row['entity_id']]);
        }

        $this->setup->getConnection()->endSetup();
    }

    private function transformData(array $row): array
    {
        return [
            'new_field' => $row['old_field'] ?? '',
        ];
    }

    public function getDependencies(): array
    {
        return [];
    }

    public function getAliases(): array
    {
        return [];
    }
}

Custom Module Upgrade

Module Compatibility Check

<?php
namespace Vendor\Upgrade\Console;

use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Magento\Framework\Module\ModuleListInterface;

class CheckModuleCompatibility extends \Magento\Framework\Console\Command
{
    public function __construct(
        private ModuleListInterface $moduleList,
    ) {
        parent::__construct();
    }

    protected function configure(): void
    {
        $this->setName('upgrade:check-modules');
        $this->setDescription('Check custom module compatibility');
        parent::configure();
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $modules = $this->moduleList->getNames();
        $customModules = array_filter($modules, function ($module) {
            return strpos($module, 'Magento_') !== 0;
        });

        foreach ($customModules as $module) {
            $output->writeln("Checking: {$module}");

            $issues = $this->checkModule($module);

            if (!empty($issues)) {
                foreach ($issues as $issue) {
                    $output->writeln("  <warning>{$issue}</warning>");
                }
            } else {
                $output->writeln("  <info>Compatible</info>");
            }
        }

        return 0;
    }

    private function checkModule(string $moduleName): array
    {
        $issues = [];
        $modulePath = $this->getModulePath($moduleName);

        // Check for deprecated methods
        $deprecatedMethods = [
            'Magento\Framework\App\ObjectManager',
            'Magento\Framework\Model\ResourceModel\Db\AbstractDb::_construct',
        ];

        foreach ($deprecatedMethods as $method) {
            $files = $this->findFiles($modulePath, '*.php');
            foreach ($files as $file) {
                $content = file_get_contents($file);
                if (strpos($content, $method) !== false) {
                    $issues[] = "Uses deprecated: {$method} in " . basename($file);
                }
            }
        }

        // Check PHP compatibility
        $phpFiles = $this->findFiles($modulePath, '*.php');
        foreach ($phpFiles as $file) {
            $output = [];
            $returnCode = 0;
            exec("php -l {$file} 2>&1", $output, $returnCode);
            if ($returnCode !== 0) {
                $issues[] = "PHP syntax error in " . basename($file);
            }
        }

        return $issues;
    }
}

Deprecated Code Migration

<?php
namespace Vendor\Upgrade\Setup\Patch\Data;

use Magento\Framework\Setup\Patch\DataPatchInterface;

class UpdateDeprecatedCode implements DataPatchInterface
{
    public function apply(): void
    {
        // Example: Update deprecated ObjectManager usage
        $files = glob('app/code/Vendor/CustomModule/**/*.php');

        foreach ($files as $file) {
            $content = file_get_contents($file);

            // Replace deprecated patterns
            $replacements = [
                'ObjectManager::getInstance()' => 'Use dependency injection instead',
                '$_objectManager' => 'Use constructor injection',
            ];

            foreach ($replacements as $old => $new) {
                if (strpos($content, $old) !== false) {
                    $this->logDeprecated($file, $old, $new);
                }
            }
        }
    }

    private function logDeprecated(string $file, string $old, string $new): void
    {
        $this->logger->warning("Deprecated code found", [
            'file' => $file,
            'deprecated' => $old,
            'suggestion' => $new,
        ]);
    }
}

Composer Upgrade Strategy

{
    "require": {
        "magento/product-community-edition": "2.4.7",
        "magento/module-invitation": "*",
        "vendor/custom-module": "^2.0"
    },
    "require-dev": {
        "phpunit/phpunit": "^10.0",
        "squizlabs/php_codesniffer": "^3.7"
    },
    "conflict": {
        "magento/product-community-edition": "<2.4.5"
    },
    "replace": {
        "magento/module-instant-purchase": "*"
    },
    "extra": {
        "magento-force": "override"
    }
}
# Upgrade commands
composer require magento/product-community-edition=2.4.7 --no-install
composer update --no-dev --prefer-dist
bin/magento setup:upgrade --keep-generated
bin/magento setup:di:compile
bin/magento setup:static-content:deploy -f
bin/magento cache:clean
bin/magento indexer:reindex

Testing and Rollback

Upgrade Test Suite

<?php
namespace Vendor\Upgrade\Test\Integration\Upgrade;

use Magento\TestFramework\Helper\Bootstrap;
use PHPUnit\Framework\TestCase;

class UpgradeVerificationTest extends TestCase
{
    public function testCoreModulesInstalled(): void
    {
        $moduleList = Bootstrap::getObjectManager()->create(
            \Magento\Framework\Module\ModuleListInterface::class
        );

        $modules = $moduleList->getNames();

        $this->assertContains('Magento_Catalog', $modules);
        $this->assertContains('Magento_Checkout', $modules);
        $this->assertContains('Magento_Customer', $modules);
    }

    public function testVersionMatches(): void
    {
        $configReader = Bootstrap::getObjectManager()->create(
            \Magento\Framework\Config\Reader\ConfigLoaderInterface::class
        );

        $config = $configReader->load('module');

        foreach ($config['module'] as $moduleName => $moduleConfig) {
            if (strpos($moduleName, 'Magento_') === 0) {
                $this->assertArrayHasKey('setup_version', $moduleConfig);
            }
        }
    }

    public function testDatabaseSchemaValid(): void
    {
        $setup = Bootstrap::getObjectManager()->create(
            \Magento\Framework\Setup\SchemaSetupInterface::class
        );

        $connection = $setup->getConnection();
        $tables = $connection->listTables();

        $this->assertNotEmpty($tables);

        // Verify critical tables exist
        $criticalTables = ['catalog_product_entity', 'sales_order', 'customer_entity'];
        foreach ($criticalTables as $table) {
            $this->assertContains($table, $tables);
        }
    }

    public function testCustomModulesFunctional(): void
    {
        // Test custom module functionality
        $repository = Bootstrap::getObjectManager()->create(
            \Vendor\CustomModule\Api\EntityRepositoryInterface::class
        );

        $searchCriteria = Bootstrap::getObjectManager()->create(
            \Magento\Framework\Api\SearchCriteriaBuilder::class
        )->create();

        $result = $repository->getList($searchCriteria);
        $this->assertNotNull($result);
    }
}

Performance Benchmark

<?php
namespace Vendor\Upgrade\Test\Performance;

use PHPUnit\Framework\TestCase;

class UpgradePerformanceTest extends TestCase
{
    private string $baseUrl = 'https://magento.local';

    public function testHomepagePerformance(): void
    {
        $startTime = microtime(true);

        $ch = curl_init($this->baseUrl);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, ['Accept: text/html']);
        curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        $duration = microtime(true) - $startTime;

        $this->assertEquals(200, $httpCode);
        $this->assertLessThan(2.0, $duration, 'Homepage should load in under 2 seconds');
    }

    public function testProductPagePerformance(): void
    {
        $startTime = microtime(true);

        $ch = curl_init($this->baseUrl . '/catalog/product/view/id/1');
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_exec($ch);
        curl_close($ch);

        $duration = microtime(true) - $startTime;

        $this->assertLessThan(3.0, $duration, 'Product page should load in under 3 seconds');
    }
}

Rollback Procedure

#!/bin/bash
# rollback-upgrade.sh

set -e

BACKUP_DIR="/var/backups/magento-upgrade"
LOG_FILE="/var/log/magento-rollback.log"

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a $LOG_FILE
}

log "Starting rollback..."

# Step 1: Enable maintenance mode
log "Enabling maintenance mode..."
bin/magento maintenance:enable

# Step 2: Restore code backup
log "Restoring code backup..."
tar -xzf $BACKUP_DIR/code-$(date +%Y%m%d).tar.gz -C /var/www/magento/

# Step 3: Restore database
log "Restoring database backup..."
mysql -u root -p magento2 < $BACKUP_DIR/db-$(date +%Y%m%d).sql

# Step 4: Restore composer.lock
cp $BACKUP_DIR/composer.lock.bak /var/www/magento/composer.lock

# Step 5: Run setup
log "Running setup..."
bin/magento setup:upgrade --keep-generated
bin/magento setup:di:compile
bin/magento setup:static-content:deploy -f

# Step 6: Clean cache
log "Cleaning cache..."
bin/magento cache:clean
bin/magento cache:flush

# Step 7: Disable maintenance mode
log "Disabling maintenance mode..."
bin/magento maintenance:disable

# Step 8: Verify version
log "Verifying rollback..."
bin/magento --version

log "Rollback complete!"

Monitoring Post-Upgrade

# monitoring-config.yml
monitoring:
  uptime:
    check_interval: 60
    timeout: 10
    alert_threshold: 3

  performance:
    response_time:
      warning: 2000
      critical: 5000
    error_rate:
      warning: 0.01
      critical: 0.05
    memory_usage:
      warning: 80
      critical: 95

  database:
    connection_pool:
      warning: 80
      critical: 95
    replication_lag:
      warning: 5
      critical: 10

  queue:
    depth:
      warning: 1000
      critical: 5000
    consumer_count:
      warning: 2
      critical: 1

Quiz

1. What is blue-green deployment?

Question 1 options

2. What should you backup before upgrade?

Question 2 options

3. What is the first step in an upgrade?

Question 3 options

Flashcards

Question

What is blue-green deployment?

Answer

Two identical environments, DNS switches between them

Question

What is the upgrade order?

Answer

Maintenance → Backup → Composer → Upgrade → Compile → Deploy → Cache → Index

Question

How do you rollback?

Answer

Restore backups, re-run setup, switch DNS back

Question

What to test after upgrade?

Answer

Core modules, custom modules, database schema, performance

Question

How long to monitor post-upgrade?

Answer

At least 24-48 hours for issues to surface

Revision Notes

Key Takeaways

  • 1. Upgrade planning includes compatibility check and risk assessment
  • 2. Blue-green deployment enables zero-downtime upgrades
  • 3. Full backup (code, DB, media) is essential before upgrade
  • 4. Test thoroughly on staging before production deployment
  • 5. Monitor closely post-upgrade and have rollback ready

Interview Tips

  • Describe the upgrade planning process and timeline
  • Explain blue-green deployment and rollback strategy
  • Discuss testing approach for version upgrades
  • Talk about handling custom module compatibility

Cheat Sheet

Upgrade Process:
  1. Assessment → Compatibility, risk, timeline
  2. Preparation → Staging, backup, scripts
  3. Development → Core, modules, extensions
  4. Testing → Unit, integration, functional, perf
  5. Deployment → Blue-green, data migration, DNS
  6. Monitoring → Metrics, issues, optimization

Zero-Downtime:
  Blue-Green → Parallel environments
  Database → Replication, read-only during cutover
  DNS → Switch between environments
  Rollback → Restore backups, switch back

Testing:
  Core modules → Module list verification
  Custom modules → Functional tests
  Database → Schema validation
  Performance → Response time benchmarks

Rollback:
  Maintenance mode → Enable
  Restore → Code, DB, media
  Setup → upgrade, compile, deploy
  Verify → Version, functionality