Tech Stack Migration — Impact & Comparison

Overview

This document covers the impact of migrating from the legacy PHP-based stack to the new Python-based stack across both the Image Processing Tool and the Admin Panel. It covers speed, performance, maintainability, scalability, and operational changes.


Stack Comparison

Layer Legacy Stack New Stack
Image Processing Tool CakePHP 3.x (PHP) Python (NumPy + OpenCV)
Image Processing Engine ImageMagick (PHP Imagick extension) OpenCV + NumPy + cairosvg
SVG Rendering Inkscape (CLI subprocess) cairosvg (native Python library)
Admin Panel Backend Laravel (PHP) + Bagisto Django (Python) + DRF
Admin Panel Frontend Bootstrap 5 + jQuery React
Product Import Artisan CLI command (manual SSH) Django Admin UI + Celery (async)
Background Jobs None (blocking foreground) Celery + Redis
Real-time UI None (page reload) WebSocket (Django Channels)
ORM Eloquent (Laravel) Django ORM
Database MySQL PostgreSQL / MySQL
Caching None documented Redis
API between systems CakePHP REST endpoints Python Django REST Framework

1. Image Processing — Speed Impact

This is the biggest change. The legacy system used ImageMagick via PHP's Imagick extension, driven by CakePHP. The new system uses OpenCV and NumPy directly in Python.

Why the New Stack is Faster

ImageMagick (Legacy)

OpenCV + NumPy (New)

Rendering Speed Comparison (Estimated)

Operation Legacy (PHP + Imagick) New (Python + OpenCV) Improvement
SVG → PNG conversion ~800ms (Inkscape subprocess) ~150ms (cairosvg in-process) ~5× faster
Single composite operation ~200ms (Imagick pass) ~20ms (NumPy vectorised) ~10× faster
Full product image render (1 view) ~3–5 seconds ~0.5–1 second ~4–5× faster
Full product (4–5 views) ~15–25 seconds ~3–5 seconds ~5× faster
2400 products × 5 views ~10–16 hours ~2–3 hours ~5× faster

These are estimates based on typical ImageMagick vs OpenCV benchmarks. Actual numbers depend on server specs and image resolution.

Why NumPy Vectorisation Matters

In the legacy system, a composite blend like Color Burn was applied as an Imagick operation — a black-box C function called once per image. In the new system:

# Color Burn in NumPy — applied to all pixels simultaneously
# No loop, no per-pixel overhead — entire image processed in one operation
result = 1 - (1 - base) / (texture + epsilon)
result = np.clip(result, 0, 1)

This runs on the entire image array at once using CPU SIMD instructions. The equivalent in PHP/Imagick has no equivalent vectorised path — it is always a sequential per-pixel or per-channel operation internally.


2. SVG Rendering — Speed Impact

Legacy: Inkscape CLI Subprocess

PHP exec("inkscape input.svg --export-png=output.png")
    → OS forks a new process
    → Inkscape loads, initialises its GTK/rendering stack (~400–600ms startup)
    → Renders SVG
    → Writes PNG to disk
    → PHP reads PNG from disk
    → Subprocess exits

Every render paid the full Inkscape startup cost (~400–600ms) even for a tiny SVG. At 2400 products × 5 views = 12,000 renders, this alone adds ~1.5–2 hours of pure startup overhead.

New: cairosvg In-Process

import cairosvg
png_bytes = cairosvg.svg2png(url=svg_path, output_width=1200)
    → Renders SVG using Cairo graphics library
    → Returns PNG bytes directly in memory
    → No subprocess, no disk write, no startup cost

No process forking. No startup cost. Output is already in memory as bytes, directly loadable by OpenCV without a disk round-trip.


3. Admin Panel — Speed & Developer Impact

Legacy: Laravel + Bagisto + jQuery

Bagisto is a full e-commerce framework built on Laravel. It comes with a large EAV (Entity-Attribute-Value) data model with many indirection layers. Every product write goes through multiple tables (products, product_flat, product_attribute_values) via Bagisto's repository pattern.

The import command had to:

  1. Call productRepository->update() through Bagisto's abstraction layers
  2. Then fix attribute text values (overwrite option IDs back to human-readable names)
  3. Then sync product_flat across all locale rows manually

This meant each product import was 5–10 individual SQL queries through multiple abstraction layers.

The frontend was jQuery — full page reloads for most interactions, no real-time feedback.

New: Django + DRF + React

Django ORM writes directly to your own clean models. No EAV indirection, no Bagisto abstraction layers. A product upsert is one SQL query via bulk_create with update_conflicts.

React gives the admin panel real-time UI — WebSocket progress, live dropdowns, no page reloads.

Admin Panel Feature Comparison

Feature Legacy (Laravel + Bagisto + jQuery) New (Django + React)
Product import trigger SSH terminal command UI button in admin panel
Import feedback Terminal stdout only Real-time WebSocket progress
Category mapping CLI positional arguments Dropdown selectors in UI
Import mode Blocking foreground Async Celery background task
Import scale Single task, serial Parallel chunked Celery tasks
Re-run on failure Full re-run from scratch Retry failed chunks only
Product upsert Per-product repository->update() bulk_create with update_conflicts
DB writes per 100 products ~500–1000 queries (EAV layers) 1 bulk query
Frontend interaction jQuery + full page reloads React SPA, no reloads
Real-time UI Not available WebSocket (Django Channels)
API between systems CakePHP REST Django REST Framework

4. Data Model — Complexity Impact

Legacy EAV Model (Bagisto)

Bagisto uses an Entity-Attribute-Value architecture inherited from Magento patterns. A single product's data is spread across:

products                    → core row
product_flat                → denormalised per channel + locale
product_attribute_values    → all attributes stored as generic key-value rows
product_images              → image paths
attribute_options           → option lookup table

Reading a product's full data requires joining 4–5 tables. Writing a product requires coordinated writes to all of them, plus a step to fix text values that Bagisto overwrites with option IDs.

New Clean Model (Django)

Product                     → all fields directly on the model
ProductImage                → image URLs with hash
ImportJob                   → import tracking
Category                    → M2M via product_categories

All product data is on one model. One query to read, one bulk query to write. No abstraction layers between your code and the database.

Query Count Comparison — Importing 100 Products

Step Legacy (Bagisto) New (Django)
Upsert 100 products ~500–1000 queries 1 bulk_create query
Assign categories 100 individual M2M inserts 1 bulk M2M operation
Sync images 100 delete + 100 insert = 200 queries 1 hash check + 1 bulk_create
Total ~800–1300 queries ~5–10 queries

5. Background Processing — Architecture Impact

Legacy

The PHP system had no background job infrastructure. The import command was run in the foreground in a terminal. If the SSH connection dropped, the import died. No retry, no progress tracking, no partial recovery.

Image generation was also synchronous — clicking "Generate Images" in the CakePHP admin blocked the web request for the duration of all renders.

New

Celery + Redis provides a proper job queue. Every long-running operation (import, image generation) is dispatched as an async task. The web request returns immediately with a job ID. The admin UI connects via WebSocket and receives real-time push updates as each chunk completes.

Legacy:  Click button → wait 10 minutes staring at spinner → done (or connection drops)

New:     Click button → instant response → WebSocket shows live progress per chunk
                     → chunk 1/24 done (100 products)
                     → chunk 2/24 done (100 products)
                     → ...
                     → Import complete — 2350 imported, 50 skipped

Failed chunks are retried automatically (up to 3×). If some chunks still fail, the job is marked partial and only the failed chunks need to be re-run — not the entire import.


6. Maintainability & Developer Experience

Single Language Across Both Systems

The legacy stack used two languages (PHP for both CakePHP and Laravel) with different frameworks, patterns, and tooling. The new stack uses Python throughout — the Image Processing Tool and the Admin Panel share the same language, the same libraries, and the same developer mental model.

Factor Legacy New
Languages PHP (CakePHP) + PHP (Laravel) + JS (jQuery) Python + Python + JS (React)
Shared code between systems Not possible (separate PHP apps) Possible (shared Python utilities, models)
Testing image processing Difficult — ImageMagick ops are opaque Easy — NumPy arrays are inspectable at every step
Debugging renders Write to disk, inspect file Inspect array values directly in Python debugger
Adding a new composite mode New Imagick call + PHP glue New NumPy expression, 1–3 lines
Frontend state management jQuery DOM manipulation React state — predictable, testable

7. Operational Impact

Dependency Changes

Dependency Legacy New Notes
Inkscape Required (CLI subprocess) Not needed Removed entirely
ImageMagick / Imagick Required Not needed Replaced by OpenCV
PHP Required (two systems) Not needed Removed entirely
Composer Required Not needed
Node / npm Optional Required (React build)
Python Not needed Required (both systems)
Redis Not needed Required Celery broker + cache + channels
Celery worker Not needed Required Background task processing

Server Requirements

Requirement Legacy New
PHP runtime Yes No
Python runtime No Yes (both systems)
Redis No Yes
Celery workers No Yes (1+ worker processes)
Django Channels (ASGI) No Yes (for WebSocket)
ASGI server (Daphne/Uvicorn) No Yes (replaces WSGI)

8. Summary — Net Impact

Factor Direction Detail
Image render speed ✅ ~5× faster OpenCV + NumPy vs ImageMagick + Inkscape
SVG conversion speed ✅ ~5× faster cairosvg in-process vs Inkscape subprocess
Product import speed ✅ ~100× faster Bulk + parallel vs serial per-row
DB query count (import) ✅ ~100× fewer bulk_create vs EAV repository pattern
Admin UX ✅ Major improvement Real-time WebSocket vs blocking CLI
Failure recovery ✅ Improved Chunk-level retry vs full restart
System complexity ➡ Similar Different complexity — job queue added, EAV removed
Server dependencies ➡ Changed PHP/Imagick/Inkscape removed, Redis/Celery/ASGI added
Codebase language ✅ Simplified Single language (Python) across both systems
Debuggability ✅ Improved NumPy arrays inspectable vs opaque ImageMagick ops
Frontend reactivity ✅ Major improvement React SPA vs jQuery + page reloads