← All guides8 min read

Re-running a Graftport migration safely on go-live night

How Graftport's run model lets you re-run a migration on launch night without creating duplicates: the run modes, the dry-run safety net, the cheaper re-sync rate, and the rollback path if something is wrong after launch.

The hardest hour of a Shopify migration is the one where you flip DNS and the new store starts taking real orders. Most platforms tell you "do not touch the migration on go-live night." Graftport is built around the opposite assumption: re-running is a normal operation, even on launch night. This guide is the contract for how that works.

The run model: extract, transform, load

Every run in Graftport is one of four shapes:

ModeWhat it does
Extract onlyRead from source, store raw. No destination touch.
Transform onlyRe-shape last extract using current mappings. No fetches.
Load onlyPush the last transformed shape to the destination.
FullExtract → transform → load, in one run.

Each mode can also be flagged dry-run, which means the run does all its work but stops short of writing to the destination Shopify store. Use dry-run liberally — it is exactly the same code path as a real run, minus the final write.

The four modes are how you orchestrate launch. A typical sequence:

  • Day -7. Run Full + dry-run to populate staging on a fresh destination dev store. Spot-check.
  • Day -1. Run Full + dry-run again on the same destination to confirm nothing has drifted. Spot-check the last 100 orders on the source.
  • Launch hour. Run Full, no dry-run. Idempotent: anything already loaded is skipped.
  • Launch hour + 1. Flip DNS once Graftport reports the run succeeded.

Idempotency: how Graftport prevents duplicates

Every record Graftport writes carries a stable identity formed from the source platform plus the source ID. Magento's product ID 7842 is magento:product:7842 regardless of which run touched it. The destination Shopify product's metafield carries the same identity tag.

On any subsequent run:

  1. Extract pulls every record from the source as before.
  2. Transform shapes it as before.
  3. Before loading, Graftport queries the destination for any record carrying the matching identity tag.
  4. If found and unchanged: skipped. The skip is recorded; the destination is not touched.
  5. If found and changed: synced. Counted at the sync rate (much cheaper than a first-load record).
  6. If not found: first-loaded. Counted at the tier rate.

You can re-run a migration ten times in a row. The destination will not gain duplicates. The bill grows by sync charges for the records that genuinely changed since the last run, and by nothing else.

The dry-run safety net

Dry-run runs the same extract and transform code as a live run. What it skips is exclusively the destination writes. Use it for:

  • Testing a mapping change. After editing a mapping, run Transform only + dry-run and inspect the output in the Data tab. Verify before you write.
  • Catching breaking source changes. If the source store's schema changes overnight (a custom attribute was deleted), dry- run will surface a transform error before you load.
  • Estimating cost. Dry-run reports the projected first-load vs. sync record counts. The pricing dashboard converts that into a cost estimate matching the current pricing plan.

A failed dry-run is cheap. A failed live load on launch night is expensive in trust, even if Graftport is fully idempotent.

The launch-night runbook

A practical hour-by-hour timetable that has worked across many launches:

T -2 hours. Run Extract only against the live source. This locks in the source state at a known timestamp.

T -1 hour. Run Transform only + dry-run. Open the Data tab, confirm record counts match expectations and no records errored. Total time: usually under 10 minutes.

T 0. Run Load only (no dry-run). Pushes the just-extracted, just-transformed payload to the destination. Most records are skipped (already loaded from the rehearsal); a few hundred sync; new orders since the rehearsal first-load. Total time: a few minutes for a delta, longer for a full first push.

T +5 minutes. Open the run page, confirm failed = 0 and the run status is succeeded.

T +10 minutes. Flip DNS to the destination Shopify store. Old URLs continue to 301-redirect via the redirect table Graftport loaded on the destination during the first run.

T +60 minutes. Submit the new sitemap to Search Console. Watch the Coverage report.

What re-running does not handle

Two cases call for human judgment:

  • Hard deletes on the source. A product that existed at first load and has been deleted from the source by go-live is not deleted on the destination. Graftport never deletes records you did not explicitly remove. If you want to mirror deletes, do them manually on the destination side, or contact us about enabling soft-delete propagation per resource type.
  • Field-level changes that need translation. If your source team renamed an attribute between extract and load, the new name needs a mapping update before the load. Run Extract only + Transform only + dry-run to surface the gap before it becomes a stuck launch.

Rollback if something is wrong post-launch

Graftport never overwrites a destination record without recording what it did. Each loaded record's per-run trace lives in run.record_event. If a re-sync clobbered a hand-edit your team made on the destination Shopify store, you can:

  1. Open the run page, find the record in Data → Run output.
  2. The "previous value" is recorded alongside the "new value".
  3. Restore by hand, or revert the mapping that caused the change and re-sync.

Larger rollback patterns:

  • Revert a mapping change. Mappings are versioned in app.mapping_template_version. From the mapping editor, click Versions, pick a prior version, hit Restore. The next run uses the restored shape.
  • Re-pause a destination. If a re-run is pushing rubbish, you can disconnect the destination credentials from the migration's settings page. The next run fails fast at the load phase rather than writing more bad data.

Cost shape on a re-run

The pricing tier you pay depends on whether a record is a first load or a sync. The dashboard's projection is based on the most recent dry-run; it is exact, not approximate. A typical launch- night sequence costs:

  • The original first load: priced at the first-load tier rates (decreasing per record after the included monthly allowance is consumed).
  • Each rehearsal re-run: only the delta records get charged. Records that did not change are skipped at zero cost.
  • The launch-night re-run: also only the delta. Most launches cost low double-digit euros above the original load.

Related reading

When go-live night arrives, the muscle memory you built in dry-runs makes it boring. Boring is what you want.

Ready to migrate?

Connect a source store, dry-run a migration, see the exact Shopify result before a single record lands. The same platform your team will use on go-live night.

Get started See the calculator
Related guides
Using the Graftport CLI with Claude Code: install to first publish
Set up the Graftport CLI and migration-engineer skill in Claude Code, then drive a real Shopify migration through the validate and publish l
Human approval gates for AI Shopify migrations: the Graftport contract
Why an AI coding agent should never push to Shopify on its own, and how Graftport's two-tier CLI contract enforces a human gate on every cos
Automating JSONata Shopify mapping with an AI coding agent
How the Graftport CLI lets an AI agent investigate source rows, fix JSONata mapping errors against structured validation codes, and publish