THE MIGRATION LINE
INCIDENT // SHOPIFY-2019 MIGRATED

Tax code at Shopify was spread across components that knew too much about how tax worked. A change in Checkout could silently break Cart.

SHOPIFY · 2019 · INFRA / COMPONENTIZATION / ARCHITECTURE / MONOLITH
System stress over time Nominal
Cart, Checkout, Order Consumers
zero Downtime
a few days Rollout
100% of shops Final reach
BEFORE

Everyone knew too much about tax

Shopify’s tax engine started out simple. Over years of development and incremental additions it became one of the harder parts of the codebase to work within — the team’s own description was “the Wild West of tax code.” Tax logic had spread far and wide, into components that knew too much about how tax calculations actually worked.

Because the internals leaked into every consumer, the components were coupled through knowledge they shouldn’t have had. Checkout, Cart, and Order all reached into tax mechanics directly, so a change in one place could ripple somewhere no one was looking.

AFTER

One clean door into taxes

The team consolidated tax logic into a single component, following Shopify’s established componentization architecture. Its goal, in their words, was to retain the complexity but eliminate the complications — the domain is genuinely hard, but the tangle around it didn’t have to be.

Access now runs through one clear entry point: a TaxesRequestSchema going in and a TaxesResponseSchema coming out, built from simple types that make no assumptions about the calling component. Three consumers — Cart, Checkout, and Order — talk to the tax component only through that door. Other developers praised the clean entry point, and Shopify’s framing is that it reduces the cognitive load and abstract knowledge needed to work with tax at all.

THE BRIDGE

Run both engines, then diff them

Before writing the boundary, the team had to find every place tax leaked. They audited the codebase with regular expressions, scripts, and manual processes to locate all tax-touching code, leaned on an in-house tax subject-matter expert, and even ran a weekly trivia question on taxes around the world to build shared knowledge. Each consumer got unit tests, integration tests, and end-to-end scenarios.

The actual cutover was a dual-run. They shipped an experiment where both the old and the new code paths fired simultaneously, compared the two calculations, and used the discrepancies to find bugs. Then they rolled out gradually — a small number of shops first, scaling up slowly over a few days until 100% of shops used the new component — with zero downtime and no merchant impact.

WHAT IT COST

The complexity didn't go anywhere

The honest note is in the team’s own goal: retain the complexity, eliminate the complications. Tax is still hard — jurisdictions, rules, and edge cases didn’t shrink. What changed is where that complexity lives. It’s sealed inside one component instead of smeared across Cart, Checkout, and Order, so consumers carry a simple request-and-response contract instead of the whole domain in their heads.

Getting there was also mostly unglamorous work: regexes and manual audits to find leaked code, layers of tests per consumer, and a diffing experiment run on production traffic. There is no reported performance headline here — the win is structural, measured in blast radius and cognitive load rather than milliseconds.

Componentizing isn’t about deleting complexity — it’s about giving it one door, so a change on the inside can’t silently reach out and break a caller that knew too much.

Source — read the original

https://shopify.engineering/componentizing-shopify-tax-engine

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