Skip to content

Planning a Shopify migration without losing data

The failures are never the products. They are the metafield values, the redirects, and the three things nobody wrote down before the cutover.

GuideJuly 1, 20268 min read

The products always arrive. That is the deceptive part of a store migration: the catalog lands, the images come across, somebody opens the new admin and it looks right. Then launch happens and the reviews are gone, the size charts are blank, the old collection URLs return 404, and the wholesale customers cannot log in because their tags never travelled with them.

Nothing was dropped dramatically. Things were dropped because nobody wrote down that they existed. A migration plan is mostly paperwork: an inventory taken before anything moves, a decision next to each line on it, and a cutover you can undo. The engineering is the easy half.

Start with the inventory, not the platform

The first artefact of a migration is not a theme and not an import script. It is a list of what the current store actually contains — taken from the store, not from memory, because memory omits exactly the things that break.

Export the catalog and count it: products, variants, the archived, the drafts. Pull the metafield definitions, and separately the namespaces that actually carry values, because a definition with no values is a shape and a definition with forty thousand values is a data migration. List the apps and what each one stores. List the customer segments and the tags they depend on. List the content and the theme settings holding copy nobody remembers editing. List the URLs that receive traffic.

The line most often missing is the one that matters most: data that lives inside an app rather than inside the store. Reviews, subscription contracts, loyalty balances, wishlists, negotiated price lists. Shopify does not hold those; the app does. If the destination uses a different app, that data moves through the vendor or it does not move at all. Ask before you pick the launch date, not after.

That list is the project. Everything after it is execution.

Structural data and transactional data are two migrations

They fail differently, they want different tooling, and running them as one job is how a migration stops being repeatable.

Structure is the shape of the store

Metafield and metaobject definitions, metaobject entries, collections, navigation, pages, blogs, theme settings, markets, shipping profiles. Structure is idempotent by nature: you can run it, look at it, fix the mapping and run it again, because re-creating a definition that already exists comes back as TAKEN rather than silently duplicating, so the migrator has to check before it writes — as long as you match by logical key and never by source ID. An ID from the old store means nothing in the new one.

The store-to-store structural migrator we built does exactly this and refuses to touch anything else. The reasoning is worth stealing: a tool that can also move orders is a tool that can also lose them.

Transactional data is the record of what happened

Orders, customers, inventory levels, gift card balances. Not idempotent, time-sensitive, and still changing while you migrate. Run it twice and you get duplicates. Run it early and it goes stale. Run it late and it becomes the critical path of the cutover.

So the sequence is not parallel: structure first and repeatedly, transactions once and last. Products sit in between — structural enough to load weeks early, transactional enough in price and inventory to need a delta pass right before the switch.

Scroll the figure sideways to see all of it

What actually has to move

Products, variants and their media

Match by SKU or by handle, never by source ID. Images are the part that quietly breaks: product records export cleanly as JSON, but the media hanging off them are URLs pointing at a store about to be switched off. Download every image during the export and re-upload it against the destination product. The catalog migration we ran off BigCommerce was, in practice, mostly this: paginate the export, capture images to disk, re-upload matched by SKU.

Metafield definitions and values, as two passes

The most common omission, and the most confusing symptom. A definition is a schema: namespace, key, type, validations, the resource it attaches to. A value is the content. Copy only definitions and you get an admin that looks complete over a storefront that renders nothing. Copy only values and you get data the admin will not display.

Definitions first, all of them, then values. The order matters again for reference types: a metafield of type product_reference stores a global ID, and a global ID from the source store points at nothing in the destination. Those fields have to be re-resolved through the logical key after both sides exist. Plan a pass for it. Do not discover it on the Friday.

Customers

Customers, addresses, tags and marketing consent move. Passwords do not. On classic customer accounts that means every customer gets a password reset, which is a communications problem before a technical one: a login that no longer works, with no email explaining why, becomes support volume for a month. New customer accounts — the default now — sign in with an emailed code, so there is no password to carry and the problem disappears. And importing a consent flag you cannot evidence is a legal decision, not a data one.

Redirects and content

Every URL with traffic or an inbound link needs a destination. Pull the list from analytics and search console rather than from the sitemap: the sitemap tells you what exists, the logs tell you what people ask for. Build the redirect map as a file and verify it as a list of assertions rather than by clicking three of them — which is most of what SEO implementation at launch actually consists of. Pages, blogs and menus move alongside it, and are consistently left until launch week.

What should not move

A migration is the only free opportunity you will get to delete things. Take it, with a decision written down per category rather than a default of carrying everything.

  • Products with no sales in two years that are not seasonal. Archive them at the source.
  • Metafield namespaces belonging to apps you are not reinstalling. That data is inert in the new store and will mislead whoever reads the schema next.
  • Theme settings from a theme you are replacing. Migrating them reproduces decisions made for a different layout.
  • Historical orders past your operational need. Contentious, so make it a decision. A defensible position is to migrate the last twelve to twenty-four months as live records and keep the full history as a queryable export.
  • Customers who have never ordered and have not opened an email in years. You are migrating a deliverability problem.
  • Every discount code that has ever existed. Migrate the active ones.

The test is whether a record has a reader. No customer, no report, no automation reads it — it is weight.

The parallel run, and a cutover you can put back

The mistake is treating launch as an event rather than an overlap. Both estates should be alive at once: the old one serving customers, the new one complete, reachable at a temporary hostname and doing real work.

Real work means orders that go all the way through payment and fulfilment, not a theme preview. Then fire the joins. Does the ERP receive the order in the shape it expects? Does the 3PL receive the fulfilment request? Does the tax engine return the right rate for the regions you care about? Those break in a way that looking at the storefront never reveals, which is why integration work belongs inside the parallel run rather than after it.

Then the delta pass: new products, price changes, inventory, and the customers and orders created during the overlap. Freeze what you can — a short catalog freeze is a normal, sane ask — and run the deltas as close to the switch as you can afford.

The cutover itself should be one small change: move the domain and watch. Small is the point. A cutover made of a single reversible change is one you can undo in the time it takes a TTL to expire, which is why the TTL comes down to a minute or two beforehand and goes back up after.

Write the rollback down before the day, and be honest about where it stops being available. It is not the DNS change. It is the first order the new store accepts and fulfils. Before that, going back costs nothing. After that, an order exists in one system and not the other and you are reconciling by hand. Knowing where that threshold sits is what lets you schedule the day sensibly instead of nervously.

Scroll the figure sideways to see all of it

The plan is the deliverable

None of the engineering here is hard. Exports paginate, imports upsert, redirects are a file. The hard part is the accounting: knowing what exists, deciding what survives, and arranging the sequence so the switch is small enough to reverse.

Write the inventory. Split structure from transactions. Test the joins rather than the homepage. Keep a way back until the first fulfilled order takes it away from you. Most migrations that go badly did not fail technically — they failed at the list, weeks before anybody wrote code. Shopify work of this kind is won or lost in that document.

Blog