Miguel Angel Meza Mendez

Systems engineer, trying to get a little better every day.

I work at Converse México’s corporate office, on its online store. Most of the time I’m in the Magento backend, but I also build parts of the React PWA, run load tests and dig into the database.

I like understanding why something fails before fixing it, and leaving tests behind so it doesn’t happen again.

Work
Converse México
Based in
Mexico City · remote
Miguel Angel Meza Mendez
19

Magento modules of my own at Converse

350k+

customers ready to migrate with their orders, carts and wishlists

98.4%

of requests from cache after fixing the 502s

291

automated tests in my own platform

Experience

Where I’ve worked and the problems I got to solve.

Converse México

May 2026 – now

Magento Developer

I joined for the Magento backend and ended up touching almost everything: native modules, the React PWA, integrations, performance testing and database analysis. I have built 19 modules of my own and changed more than 30 existing ones.

Adobe Commerce 2.4.8 Cloud PHP 8 GraphQL MySQL Fastly PWA Studio React Apollo Jest Cypress k6 Botpress
data

Migrating more than 350,000 customers with their full history

Moving from the old store to the new one means bringing every customer over with their addresses, orders (with invoices, shipments and credit memos), wishlist and active cart. Without losing anything, in a single maintenance night.

Ready for production. In the dry run: 5.4 million rows, including 184,000 orders, 151,000 invoices, 145,000 shipments, 258,000 wishlists and 14,000 active carts, without losing a record.

How I solved it
  • Python scripts that read the mysqldump backups directly and stream them table by table. The first version loaded everything into memory and died halfway through, out of RAM.
  • Remapped the custom customer and address attributes (phone number and ten Mexican address fields) to the new platform’s IDs, and automatically dropped the columns the new one doesn’t have.
  • A final step that reconnects orders, wishlists and carts to the new catalog by SKU.
  • In one of the runs, the 2.25 GB SQL file arrived corrupted on the server and the import broke halfway. Since then everything travels compressed and is checked with sha256 before and after.
  • Found and fixed a bug in my own parser: it only read the first INSERT block of each table and counted 2,867 customers instead of hundreds of thousands.
  • Wrote down the step-by-step procedure for migration night: maintenance mode, fresh backup, cleanup, import and remapping.

Built with: Python, mysqldump, SQL, gzip, sha256

performance

The product page couldn’t be cached and the store started throwing 502s

A plugin put the session ID into the HTTP context, so Fastly stored one version of the page per visitor. The reviews template also printed customer data into shared HTML. Under heavy traffic, PHP couldn’t keep up.

k6 load test (10 users, 10 minutes): a 98.4% cache hit ratio over 43,000 requests, 0 5xx errors, and category pages dropped from 636 ms to 93 ms.

How I solved it
  • Removed the session ID from the HTTP context and generated it in the browser instead.
  • Customer data is now filled in by JS from customer-data, not by the cached template.
  • Added per-product cache tags to our GraphQL queries so Fastly only purges what changes.
  • Versioned the search JS, which stayed cached for a year with no way to invalidate it.

Built with: Magento plugins, GraphQL @cache, Fastly, k6

module

Pre-sales were run with manual SQL

Opening sign-ups, moving products into a restricted category and granting or removing permissions for each customer group was done with direct UPDATEs on the database.

About 3,000 lines that replaced the old module and the manual SQL.

How I solved it
  • A new module with a five-state machine (scheduled, sign-up open, sign-up closed, sale active, closed) driven by a cron job every 5 minutes, with fallback buttons in the admin.
  • It stores each customer’s original group to restore it when the campaign closes.
  • It works with Adobe Commerce catalog permissions and reindexes right away instead of waiting for cron.
  • A storefront widget to sign up and a PDF export of everyone registered.

Built with: db_schema, cron, observers, UI Components, CatalogPermissions

operations

Cleaning up junk accounts without touching real customers

The database had bots, test accounts, unconfirmed and abandoned accounts mixed in with real customers.

Running in production.

How I solved it
  • Seven pluggable SQL filters wired through dependency injection, with a preview in the admin.
  • A database queue processed by cron in batches. Right before deleting, it checks again whether the customer bought something or logged in while waiting.
  • Deletes through Magento’s official repository so native events still fire.
  • Along the way, accounts that sign in with Google or Facebook are now marked as confirmed.

Built with: DI, db_schema queue, cron, console command

operations

Marking orders as delivered, one by one

Files with thousands of orders to mark as delivered were handled by hand.

Running in production.

How I solved it
  • Upload a CSV in the admin; only orders that are processing and fully invoiced are touched.
  • If an order has no shipment, it creates one. With multiple inventory sources it failed when deducting stock, so I fixed it by assigning the right source.
  • Anything that doesn’t qualify goes into a CSV of rejects, and every order gets an audit comment.

Built with: Sales, Shipments, MSI

security

Google sign-in that failed and wrong account menus

The Google callback failed the anti-CSRF check because the state only lived in the session, and the account menu showed the wrong state because it came from a shared cache.

How I solved it
  • Stored the state in a sensitive cookie as well and compared it with hash_equals.
  • The account menu is now rendered client-side.
  • Logout moved to POST with a form_key.

Built with: OAuth, customer-data, RequireJS

pwa

Card installments in the PWA

The PWA card form never offered installments; only the PayPal button did, in its own popup.

About 400 lines of tests. Tested in sandbox with a new Visa at 3 months and a saved AMEX at 6.

How I solved it
  • On the backend, a GraphQL query that gets PayPal’s client token and a fix so saved cards kept the selected term.
  • On the front, Card Fields with the terms the bank offers, respecting the admin minimums, for new and saved cards.

Built with: React, PayPal JS SDK, GraphQL, Jest

pwa

The cart let people add items that weren’t in stock

The + button allowed quantities that Magento later rejected with “Not enough items for sale”.

How I solved it
  • Exposed the sellable quantity of the selected size through GraphQL.
  • A custom quantity stepper that stops at the limit on the same click.
  • Separated stock errors from minimum and multiple errors so it doesn’t block too much.

Built with: React, Apollo, GraphQL, Jest

integration

A support chatbot without paying for AI

They wanted a support chatbot without paying for calls to an AI model, and with a way to reach a person.

Works end to end with no per-call AI cost; AI will be added only for specific cases.

How I solved it
  • A Botpress bot with a menu: stores, orders, shipping, products and talk to someone.
  • Hand-written OAuth 1.0a signing to call Magento’s REST API.
  • Handoff to a human agent in LiveChat through its chat API and webhooks, which Botpress doesn’t ship.
  • In the PWA, the bubble can be dragged on touch screens only.

Built with: Botpress ADK, TypeScript, OAuth 1.0a, LiveChat, Pointer Events

quality

Measuring the PWA on a slow phone

There was a performance audit of the PWA and nobody had checked what was actually true.

Confirmed 4 findings, ruled out 2 and found 3 new risks; one was an update banner shown to almost every new visitor.

How I solved it
  • Measured it with k6 browser in a real Chromium, emulating 4G and a CPU four times slower.

Built with: k6 browser, Chromium

front

Infinite loading and broken third-party scripts

A loader hung because a script loaded twice and the Content Security Policy blocked inline scripts. The Dotdigital and Adobe recommendations scripts also crashed on the product page.

Running in production.

How I solved it
  • A CSP-compatible loader without the double load.
  • Two small modules that patch the third-party scripts without touching vendor/.
  • Cypress tests to reproduce each failure before fixing it.

Built with: CSP, RequireJS, Cypress

QACi

Jun 2024 – Feb 2026

Systems Engineer

I was the only IT person. Besides supporting the computers and the WordPress site, I rebuilt their certificate system and website in Laravel 12, both still in use.

Laravel 12 PHP 8 MySQL Blade Bootstrap 5 FPDI QR Power BI
migration

From WordPress and plain PHP to Laravel without breaking folios

The website ran on WordPress and the certificate system was plain PHP spread across folders, with concatenated SQL and passwords stored in plain text.

How I solved it
  • A single Laravel 12 project with the public website and the internal panel.
  • Parameterized queries, bcrypt passwords, validation, CSRF and protected routes.
  • The search checks both the old and the new database, so certificates already issued still validate.

Built with: Laravel 12, Query Builder, MySQL

automation

Certificates that were filled in by hand

Each certificate was put together by hand or with a fragile generator.

17 official templates for five ISO standards.

How I solved it
  • A form generates the PDF on each standard’s official template, with the brand typeface and the standard’s color.
  • Every certificate carries a QR code pointing to its verification page.
  • The same flow in English.

Built with: FPDI, FPDF, endroid/qr-code

workflow

The certificate lifecycle

How I solved it
  • It starts as a draft; when released it’s regenerated with the final template and a new QR code.
  • It can be shown or hidden, renewed, and marked as expired or cancelled.
  • The public lookup shows the PDF that matches each state.
automation

Certification start letters

How I solved it
  • A PDF on the official letterhead with the text built from the company name, tax ID and address.
  • One or several standards per letter (integrated system), with a QR code.

Built with: FPDI, MySQL

form

Requests arrived with no structure

Quote requests came in by email, each one with different information.

How I solved it
  • A multi-step form with about 133 fields, where the questions change depending on the standard the client picks.
  • It notifies five internal inboxes right away and the panel keeps the detail per standard.

Built with: Blade, JavaScript, Bootstrap 5

website

News with likes and comments

How I solved it
  • A panel to publish news posts with multiple images.
  • A public feed with likes and comments without an account, limited to one per browser.

Built with: JavaScript, fetch, SweetAlert2

data

How companies change after certification

How I solved it
  • Power BI dashboards comparing each company before and after certification and process improvements.

Built with: Power BI, SQL

Projects

Things I’ve built on my own and with a team.

MJ Nexus website
website · seo

MJ Nexus website

The website of the agency I co-founded with José: websites, systems, apps and automation for small businesses.

  • Local SEO built into the code: structured data (JSON-LD), Open Graph, sitemap and robots.
  • Listed on Google Business Profile, Search Console, Bing Places and Bing Webmaster.
  • Contact form with a honeypot and rate limiting against spam.
Laravel 13 Blade Tailwind see the site →
Blisse MX
store · in progress

Blisse MX

My online store for American clothing. It’s already live; products and checkout come next.

  • Stock by size and color; a one-of-a-kind piece is just a variant with stock 1.
  • The bag works without an account and merges into yours when you log in.
  • Women, Men and Unisex categories, homepage carousels and its own admin for products and sections.
  • Photos are optimized on upload (max 1600 px, JPG).
Laravel 13 Tailwind MySQL GD see the store →
Training app
app · pwa

Training app

I built it so clients and coaches can keep routines, meals and progress in one place, from their phone. It has about 50 active users two months after launch.

  • History is never overwritten: every change to a routine or meal plan closes the previous version, so past weeks and stats never shift.
  • Coaches can only see and edit their own clients, enforced by access policies and covered by tests.
  • It installs as an app (PWA), is built touch-first and converts iPhone HEIC photos in the browser.
  • Coach–client chat, notifications and a weekly summary generated as an image.
Laravel 13 PHP 8.4 MySQL Tailwind PWA PHPUnit see the app →
team of 3 · in use

Dragon Gym

For a gym that needed to keep track of sales, inventory and memberships. Three of us built it, and they still use it.

  • My part: inventory and the point of sale in the dashboard, and bank-transfer membership payments with a receipt in the app.
  • A PHP web dashboard with a custom MVC: a point of sale that updates inventory in a single transaction, a daily cash close and seven statistics charts.
  • Excel and PDF reports, password recovery by email and admin and coach roles.
  • A Flutter app for members: SMS sign-in with Firebase and a PIN, live gym occupancy, promotions with barcodes and membership payment with a receipt photo.
  • The app and the dashboard share the same database through a REST API.
PHP MySQL Flutter Dart Firebase

Off the clock

What I do when I’m not coding.

Gym

every day

Soccer

Basketball

Video games

competitive

Contact

Got a role or a project in mind? Write to me at mezamendezmiguel@hotmail.com or on WhatsApp; I usually reply the same day.