DEV Community

Cover image for We swapped the entire backend of a live store. The frontend never found out.
Kryeo
Kryeo

Posted on Edited on

We swapped the entire backend of a live store. The frontend never found out.

Picture replacing a car's engine while someone is still driving it down the highway. No pit stop, no "back in two weeks" banner, and the driver shouldn't feel a thing.

That was the brief for Increddy, a storefront selling game and software license keys. It ran on Shopify. The commerce logic the business actually needed didn't fit Shopify well, so the backend had to go: cart, checkout, payments, inventory, all of it.

The frontend had to stay. Same Next.js app, same Zustand stores, same React Query hooks, same customers who'd never know anything changed.

Here are the three calls that made that work, and the one that sounds reckless but turned out to be the safest.

1. We didn't write a spec. We read the frontend.

The textbook move is a specification document: list the endpoints, define the payloads, hand it to the backend team.

We skipped it.

A spec written before the build is a guess about what the frontend needs. The frontend itself is the truth. So before writing any backend code, we went through the live frontend feature by feature and wrote down exactly what it called and exactly which fields it read off every response.

That became the contract. Not what a commerce platform usually returns, but what this frontend actually reads.

Specs drift from reality the moment someone finds a requirement mid-build. Code that's already running doesn't drift.

2. Two repos, two owners, one contract

The frontend and backend lived in separate repositories, owned by different people, built in parallel.

What kept them from drifting apart was a pair of boring documents: a gap tracker mapping every frontend data need to a backend capability, and matched task lists with dependency IDs so each side knew exactly what it was blocked on.

The documents weren't the real secret, though. (That story is its own post in this series.)

3. We migrated zero orders. On purpose.

This is the one people push back on.

No Shopify orders. No customers. No catalogue rows. The new Medusa v2 backend was built and populated from scratch.

It sounds like throwing away history. But think about where replatforms usually hurt: a migration script that quietly maps some edge-case record wrong, and you find out from a support ticket three weeks after launch.

Increddy's backend was being rebuilt, not translated. Starting clean removed the riskiest step entirely instead of trying to make it safe.

The part nobody puts in the pitch deck

Leaving Shopify means everything it did invisibly becomes code you write: multi-currency, payment orchestration, inventory logic. For a license-key store it also means building the one thing no platform gives you, a system that can never sell the same key twice.

That is where the real engineering went, and it's where this series goes next.


The full case study, with the architecture, the results, and every module we built, is on our site: How we migrated Increddy to a self-hosted Medusa backend →

For the step-by-step migration write-up, including how the gap tracker was structured, read the long version on the Kryeo blog.

We're Kryeo. We build headless commerce on Medusa, Directus and Next.js. If you're weighing a move off Shopify, our Medusa development page is a good place to start.

Top comments (1)

Collapse
 
elias_0549e0e2ef8cf6ad89a profile image
RAMO •

Reading the live frontend instead of writing a spec is the part that makes the swap invisible. The Next app already had the contract: which fields the Zustand stores and the React Query hooks actually read. A cart payload can share the usual commerce names and still be wrong if one of those fields is a display price rather than the unit price, and a spec written up front would have guessed.\n\nZero orders, zero customers, zero catalogue rows is the decision people will argue with, and for a license-key store it is the safe one. The failure mode you describe is the real one: a migration script that maps an edge-case record wrong and shows up as a support ticket three weeks later. There is no shipment a buyer needs reopened. The piece no platform gives you, a key that can never be sold twice, is the actual system, and starting clean means that rule does not have to survive a bad import. I would not copy the empty catalogue onto a physical store, where the old product rows are what the existing frontend already reads. For keys, dropping the history removes the riskiest step instead of trying to make it safe.