← Back to writing

Field notes · Migration

Migrating a live ecommerce from a custom solution to SaaS

What we did to swap the engine of a moving car without spilling the coffee, losing the orders, or angering the SEO team.

12 min read·Feb 2026·Migration

Bookelicious had been running on a custom PHP stack for the better part of a decade. It worked. It had also accumulated the kind of technical debt where every new feature cost three times what it should and nobody on the team could give a confident estimate on anything that touched payments, taxes, or stock.

After a few months of audit and arguing, the team agreed on a direction nobody loved at first: drop the custom store, move to a SaaS ecommerce backbone, and keep our editorial layer (recommendations, school program, classroom tools) on a clean Django app on top. This post is the short version of how we did it without taking the site down or losing a single order.

The constraints

Three things made this harder than a textbook migration:

  1. Live traffic. Schools were ordering daily: books, class packs, gift cards. No maintenance windows longer than 15 minutes.
  2. SEO equity. Years of category and product pages ranking well. Any URL or schema change had to be planned, not improvised.
  3. Editorial workflows. Teachers, librarians and editors had habits. The new admin had to feel better, not just newer.

The plan, in three phases

We did not try to do a big-bang cutover. The plan was almost embarrassingly cautious, and that's exactly why it worked.

Phase 1: Shadow mode

We stood up the new SaaS store in parallel with the legacy site, syncing inventory and orders one-way (legacy to new). Nothing customer-facing changed. The point was to validate that the new system could actually represent our data: variants, bundles, school accounts, discount stacking, without surprises. We found three.

Lesson
Don't trust the SaaS product page. Get a sandbox, push three weeks of real orders through it, and watch what breaks. The vendor's "of course we support that" is doing a lot of work in the docs.

Phase 2: Read-side cutover

We moved the storefront to the new system first, but kept order capture on the legacy backend for another two weeks. Customers saw the new pages. Orders still went through the old pipes. If anything went wrong, the rollback was a single DNS change.

This is where the SEO work paid off. We mapped every legacy URL to a new one, set up 301s before the switch (not after), kept Schema.org JSON-LD bit-for-bit identical, and watched Search Console daily for two weeks. We dropped one rank position on a handful of long-tail queries and got it back within a month.

Phase 3: Write-side cutover

Once we trusted the new storefront, we flipped orders. The trick was to make this invisible: same checkout URLs, same email templates, same Stripe account. To the customer, nothing changed except that pages loaded faster.

The boring parts that mattered most

  • Tax and shipping rules: got a full audit a week before cutover. We found three states where rates had drifted between the two systems. Two were the SaaS being right.
  • Order numbering: kept the legacy prefix to avoid confusing customer support. New SaaS got configured with our format, not its default.
  • Email deliverability: warmed up the new sending domain three weeks ahead. The day we cut over, none of our transactional mail went to spam.
  • Refunds and disputes: pre-built a tool to issue refunds against legacy orders through the new system. Without this, support would have lived in two admin panels for six months.

What I'd do differently

We over-invested in a custom data migration script when we could have used the SaaS vendor's CSV importer for 80% of the work. Engineers, including me, love a well-typed ETL pipeline. The CSV file does not care about your types.

We also under-invested in the editor experience for the first month after launch. The admin worked, but it didn't feel good, and editorial teams notice that immediately. Spend a week polishing the admin before you flip the switch, not after.

The numbers

Six weeks from shadow mode to write-side cutover. Zero hours of downtime. One bad weekend of SEO jitter. Three years of debt paid off.

The new stack ships features in days instead of weeks. The platform handles the school program's holiday peaks without us holding our breath. And the team got their evenings back.