Magento 2 Migration with Hyvä Performance Theme
Complete platform migration from Magento 1 to 2.4 for an outdoor retail business. Featuring 35+ custom modules, modern Hyvä theme with AlpineJS and Tailwind CSS, sophisticated CI/CD automation, and specialised pre-order management.
Project Overview
This case study examines a complete Magento 1 to Magento 2 migration for an outdoor retail business, encompassing platform modernisation with a focus on performance, maintainability, and business process automation. The project delivered a production-ready e-commerce platform featuring over 35+custom modules, a modern high-performance theme implementation, sophisticated CI/CD automation, and specialised business logic for pre-order management.
Show a modern high-performance theme implementationHide a modern high-performance theme implementation
The migration achieved its dual objectives: successful platform transition whilst simultaneously improving performance through modern frontend architecture, automated quality gates, and optimised development workflows.
Show automated quality gatesHide automated quality gates
fail-fast: false, developers see all violations at once rather than fixing one issue and waiting 3 minutes to discover the next. Total pipeline time: ~60sfor static tests, approximately 3-4minend-to-end.Key technical achievements include complete pre-order business automation with reporting and a performance-focused theme implementation using modern PHP 8.1+ patterns with strict type declarations throughout.
Show strict type declarations throughoutHide strict type declarations throughout
declare(strict_types=1). Modern PHP 8.1+ features including enums, readonly properties, and constructor promotion were used throughout. This provides compile-time type safety and catches type-related bugs before runtime.Technical Context
The project targeted Magento 2.4 Community Edition running on PHP 8.1 with strict type declarations throughout. The backend used Magento's architectural patterns to ensure upgrade compatibility.
Show magento's architectural patternsHide magento's architectural patterns
Magento\Checkout\CustomerData\AbstractItem, not by rewriting the class.For the frontend, the team replaced Magento's default Luma theme with the Hyvä framework, a fundamental architectural decision. Hyvä uses Alpine.js for reactivity and Tailwind CSS for styling, dramatically reducing JavaScript complexity compared to the traditional RequireJS-based approach.
Show a fundamental architectural decisionHide a fundamental architectural decision
Infrastructure Stack
- Database: MySQL 8.0 for primary data storage
- Search: Elasticsearch 7.17.8 for catalogue search
- Caching: Redis 6.2 for sessions and cache, Varnish 7.1 for HTTP caching
- Message Queue: RabbitMQ 3.11 for asynchronous operations
- Development: Docker with Warden for consistent environments
The project comprised 35+custom modules covering business logic, integrations, and optimisations, including payment gateway integrations. Theme customisation required 99override directories across Magento core and third-party modules to achieve Hyvä compatibility.
Show payment gateway integrationsHide payment gateway integrations
Technical Challenges
Challenge 1: Third-Party Module Compatibility with Hyvä
The Hyvä theme uses Alpine.js instead of Knockout.js, breaking all third-party modules designed for Luma. Evidence: 99theme override directories required to achieve compatibility.
Solution: Created compatibility layers for each integration using Hyvä's external script pattern. For example, the Klarna integration required an external script pattern that initialises payment functionality without breaking Alpine's reactivity model.
Show hyvä's external script patternHide hyvä's external script pattern
init-external-scripts event, allowing third-party JavaScript to initialise after Alpine loads. For Klarna, this meant creating a script element dynamically, setting its source to Klarna's CDN URL, adding data attributes for configuration, and appending to the document head. The once: true, passive: true event listener options ensure the script only loads once and doesn't block the main thread.Challenge 2: Pre-Order Attribute Inheritance
The pre-order extension only assigns attributes to the default attribute set. Enterprise Magento implementations often have multiple custom attribute sets that don't inherit these attributes. Products created with non-default attribute sets lacked pre-order functionality entirely.
Show the default attribute setHide the default attribute set
Solution: Data patch that programmatically assigns attributes to all existing attribute sets. The implementation iterates through all attribute sets for the catalog_product entity type, locates the appropriate attribute group, and assigns the pre-order attributes with correct sort ordering.
Challenge 3: Configurable Product Pre-Order Status
Cart items for configurable products contain the child SKU but reference the parent product object, causing pre-order status lookup failures. Pre-order labels were missing from the cart for configurable product variants.
Show configurable productsHide configurable products
Solution: ViewModel that resolves the actual child product when needed. The implementation checks if the product is configurable type, and if so, fetches the child product by SKU before retrieving pre-order data.
Show fetches the child product by SKUHide fetches the child product by SKU
$this->productRepository->get($item->getSku()). This retrieves the actual simple product (the variant) rather than using the configurable parent. The pre-order extension stores pre-order attributes on simple products, so this fetch is necessary to get the correct data. Graceful error handling ensures the cart doesn't break if the product lookup fails.Technical Approach
Hyvä Theme Migration & Customisation
Traditional Luma theme performance was inadequate for modern e-commerce expectations. The RequireJS-based architecture creates significant JavaScript overhead. The team opted for complete theme replacement with the Hyvä framework.
Show requireJS-based architecture creates significant JavaScript overheadHide requireJS-based architecture creates significant JavaScript overhead
Key implementations included 7custom view models encapsulating business logic (397 lines), 19Tailwind component CSS files for modular styling, a responsive hero image system, and a checkout fallback theme.
Show a responsive hero image systemHide a responsive hero image system
afterSave() hook on the configuration model triggers immediate generation when administrators save new hero images. This eliminates first-load resize delays. The first visitor after a content change gets pre-optimised images. WebP conversion happens simultaneously with graceful fallback for older browsers.Performance optimisations extended beyond images to the checkout process itself, requiring a robust fallback strategy for maximum compatibility.
Show a checkout fallback themeHide a checkout fallback theme
CI/CD Pipeline & Code Quality Automation
Manual code review was insufficient for maintaining quality across 35+modules. Risk of regression with each deployment necessitated automated quality gates.
The GitHub Actions pipeline features 5parallelised static analysis jobs and integration testing. The matrix strategy runs simultaneously with fail-fast: false ensuring complete visibility into all violations.
Show matrix strategyHide matrix strategy
fail-fast: false setting ensures all jobs complete even if one fails, providing developers with complete visibility into all violations. Without this, developers would fix one issue, wait 3 minutes, discover another, and repeat.PHPUnit configuration enforces strictness with multiple quality flags, preventing untested code from merging.
Show multiple quality flagsHide multiple quality flags
beStrictAboutCoversAnnotation ensures tests document what they cover. failOnRisky catches tests with risky conditions. failOnWarning prevents warnings from being ignored. forceCoversAnnotation prevents untested code from merging. Every test must explicitly declare what it covers. These settings create significantly higher quality standards than PHPUnit's defaults.Pre-Order System Implementation
Complex pre-order business requirements were not supported by the standard extension. The need for automated reporting and visual differentiation of pre-order products led to a two-module system extending third-party pre-order functionality.
Show need for automated reportingHide need for automated reporting
bin/magento preorder:report:generate) allows on-demand generation. File locking prevents concurrent report generation from corrupting output.Module 1: Attribute Management & Checkout Integration - Plugin adds pre-order data to cart items, handling the complexity of configurable products by resolving child products when necessary. Graceful error handling ensures failures don't break the checkout experience.
Module 2: Automated Reporting - Data transfer objects using PHP 8.1 readonly properties with constructor promotion for immutability. Report generation includes file locking for concurrent access safety. Weekly automated CSV reports are emailed to configured recipients, with CLI command available for on-demand generation.
Show data transfer objects using PHP 8.1 readonly propertiesHide data transfer objects using PHP 8.1 readonly properties
ReportLine uses public readonly string $sku instead of a private property with a getter method.Modern PHP 8 patterns did more than reduce boilerplate. Constructor property promotion with readonly modifiers created immutable value objects that the type system could verify at compile time. This eliminated an entire class of runtime errors before code ever reached production.
Type-safe dependency injection meant the container could validate the entire object graph during compilation. Missing dependencies became build failures, not customer-facing errors. The same patterns that accelerated development also enabled aggressive automation.
With immutable data structures and compile-time validation in place, the team could trust automated quality gates to catch regressions. CI/CD was not just deployment automation. It was architectural validation.
Quality automation creates a feedback loop that changes how developers approach architecture. When every commit triggers five parallel static analysis tools, code that passes those gates becomes inherently more maintainable. Patterns that satisfy automated checks also happen to be patterns that resist bugs.
The CI pipeline did not just catch violations. It taught the team which patterns worked at scale. Dependency injection passed type checks cleanly. Plugin architecture satisfied architectural analysis. Code that the tools approved was code that other developers could understand six months later.
This validation gave the team confidence to build complex customisations without fear. Pre-order logic could extend checkout behaviour through plugins rather than core modifications. The same automated quality gates that accelerated deployment also enabled aggressive extension of Magento functionality.
Measurable Outcomes
Code Quality Metrics
- 35+custom modules built following Magento architectural patterns
Show magento architectural patternsHide magento architectural patterns
di.xml, module.xml, events.xml) rather than hardcoded logic.- 100%strict types - all PHP files use strict type declarations
- 99theme override directories for third-party compatibility
- 15test files covering critical business logic
- 6static analysis tools in the CI pipeline
CI/CD Performance
- Build job duration: ~60-90s total time
Show ~60-90s total timeHide ~60-90s total time
composer.lock hash ensuring dependencies only re-install when changed.- Static tests (parallel): ~60scompletion time
- Integration tests: ~120-180s with MySQL + Elasticsearch
Show ~120-180s with MySQL + ElasticsearchHide ~120-180s with MySQL + Elasticsearch
- Total pipeline: ~3-4minend-to-end
Configuration Complexity
- PHPUnit configuration: 89 lines
Show 89 linesHide 89 lines
forceCoversAnnotation setting alone prevents untested code from merging. Significantly higher quality than PHPUnit defaults.- PHP CS Fixer rules: 47lines (19 rules)
- PHPMD ruleset: 49lines (14 rule groups)
- PHPStan configuration: 50lines
- GitHub Actions workflow: 151lines
These metrics demonstrate a thorough approach to quality automation, with significant configuration investment yielding consistent, maintainable standards enforcement.
Show configuration investmentHide configuration investment
Skills & Capabilities Demonstrated
Technical Skills
- Magento 2 Architecture: 35+custom modules following Magento patterns including repository pattern, dependency injection, and plugin architecture
- PHP 8.1+ Development: Strict types, enums, readonly properties, constructor promotion throughout
- Frontend Modernisation: Hyvä theme with Alpine.js and Tailwind CSS replacing traditional Luma patterns
- CI/CD Engineering: GitHub Actions with matrix strategy and parallel execution
- Test Automation: PHPUnit with integration tests and strict coverage requirements
- Plugin Architecture: Clean extension points without core modifications ensuring upgrade compatibility
- Performance Optimisation: WebP conversion, image preloading, Varnish caching
- Docker/Containerisation: Multi-service development environment with Docker and Warden
Domain Knowledge
- E-commerce: Payment gateway integrations (Klarna, SagePay, Braintree), checkout flows
- Inventory Management: Pre-order systems, backorder handling, manufacturer-specific lead times
- Retail Operations: Automated reporting, manufacturer-specific logic
- Migration Strategy: Magento 1 to 2 platform transition methodology
Notable Decisions
Checkout Fallback Theme: Separate Luma-based fallback theme specifically for checkout on incompatible browsers. Checkout is revenue-critical. A customer unable to complete purchase due to JavaScript incompatibility is unacceptable.
Pre-Generate All Hero Image Variants: Generate all responsive variants immediately when admin saves configuration rather than on first request. First-load performance critical for hero images (often LCP element). Evidence: three breakpoint variants (640px, 1024px, 1536px) generated on save.
Non-Blocking Matrix Strategy: fail-fast: false runs all 5static analysis jobs regardless of individual failures. Development velocity improved when developers see all violations at once rather than fixing one, waiting 3 minutes, finding another.
Readonly Properties for Data Objects: PHP 8.1 readonly properties with constructor promotion for all data objects. Immutability prevents accidental mutation bugs. Constructor promotion reduces boilerplate from approximately 40 lines to 10.
Related Services
How we can help with your Magento and e-commerce projects
Ready to eliminate your technical debt?
Transform unmaintainable legacy code into a clean, modern codebase that your team can confidently build upon.