THE MIGRATION LINE
INCIDENT // SHOPIFY-2020SHIPPED

Shopify's Shop model had grown past 3,000 lines into a God Object. Instead of a risky big-bang rewrite, they grew a new system over the old one and let the legacy code get strangled away.

SHOPIFY·2020·REFACTORING / RAILS / LEGACY / PATTERNS·✓ Sourced — shopify.engineering
System stress over timeNominal
3,000+ linesShop model
God ObjectShape
Strangler FigPattern
7Steps
SITUATION

The class that knew too much

Inside Shopify’s Rails codebase, the Shop model had grown to more than 3,000 lines of code — a classic God Object, a single class holding far more responsibility than any one class should. Shopify’s Kernel Architecture Patterns team took on the job of breaking it up.

The concrete example they walk through is small on purpose: extracting the store settings tied to Shopify Capital, specifically a locked_settings attribute, out of the Shop model and into its own home.

COMPLICATION

Why you can't just rewrite it

A class this central is load-bearing. Code across the whole application reads from and writes to it, so any attempt to replace it wholesale is a high-risk, all-at-once change to something the live system depends on continuously.

To even find the worst offenders, Shopify leaned on the Flog metric, which scores code by ABC complexity — assignments, branches, and calls — to point at the areas most in need of refactoring.

QUESTION

How do you replace a live system without turning it off?

So here is the question the pattern answers. How do you refactor something the application uses constantly — reading and writing, in production, all day — into a cleaner design, without a maintenance window, without a risky cutover, and without losing data along the way?

ANSWER

Grow the new system over the old one

The answer is the Strangler Fig pattern, named after Martin Fowler’s writing: a new system slowly grows over top of an old system until the old system is “strangled” and can simply be removed. Shopify applies it in seven steps. Define an interface for the new system; replace the old calls with that interface; create a new data source; write to both the old and new sources at once; backfill the new source from the legacy data; switch readers over to the new source; then remove the legacy code and stop writing to the old source.

The safety lives in the details. Writes to both sources are transactional so the two stay in sync, backfill uses pessimistic locking to stay consistent under concurrent updates, and the data migration runs on Shopify’s open-source Job Iteration API so it can move at scale without losing its place. Business logic goes into plain Ruby objects, kept separate from the persistence infrastructure.

TAKEAWAY

Incremental beats heroic

By extracting locked_settings this way, Shopify chipped a well-defined piece off a 3,000-line God Object while the application kept serving traffic and monitoring stayed green throughout. The same seven steps then apply to the next responsibility, and the next.

The transferable idea isn’t the specific tooling — it’s the shape of the move: replace a system in slices, keeping both alive until the new one has fully taken over.

The safest way to remove a load-bearing system is never to remove it — it’s to grow its replacement around it until, one day, there’s nothing left for the old one to hold up.

Source — read the original

A plain-language, AI-drafted and human-edited retelling of the article published on shopify.engineering, reorganized and explained in our own structure and words, with original analysis in the editor's note above. The facts, numbers, and decisions belong to the original author and are not altered. For the full depth, read the source.

← All systems