Converse México
May 2026 – nowMagento 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.
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
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
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
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
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
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
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
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
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
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
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