Loading...

How Shopify Actually Works

A deep dive into Shopify's architecture, why one of the biggest Ruby on Rails codebases on earth chose to stay a monolith instead of splintering into microservices, how 'pods' turned a single database problem into thousands of small, isolated ones, and the scriptable load balancers that keep checkout alive when a million buyers show up in the same sixty seconds.

By udthedeveloper•13 min read•
4 views
How Shopify Actually Works

Somewhere around 8:59pm on Black Friday, a merchant selling limited-edition sneakers drops a restock. In the next sixty seconds, tens of thousands of people hit "Buy Now" on the exact same product page, on a store that normally sees a few hundred visitors a day. On Black Friday 2024 alone, Shopify's infrastructure processed 173 billion requests in a single day, peaking at 284 million requests per minute. Nothing about that number is survivable with a single database and a single application server. The actual engineering story isn't really about handling more traffic it's about how Shopify learned to fail small instead of failing big, splitting one enormous shared platform into thousands of tiny, boring, mostly-independent slices that can each fall over without taking the others down with them.

This article looks at how Shopify is actually built to make that possible: the "pods" architecture that turned a single, terrifying database into thousands of isolated ones, the surprisingly contrarian decision to remain a monolith while most of the industry chased microservices, and a genuinely great engineering story straight from Shopify's own tech blog about teaching a load balancer to run its own checkout queue. Shopify maintains one of the most detailed public engineering blogs in the industry, so most of this draws directly from Shopify Engineering's own writing, along with independent technical breakdowns filling in the surrounding context.

Illustration of massive concurrent traffic hitting a commerce platform

One Database That Outgrew Itself, and the Pods That Fixed It

A technical breakdown on ByteByteGo lays out the timeline plainly: Shopify has used MySQL as its primary relational database since the platform's early days, but as merchant volume and transactional throughput grew, the limits of a single instance became unavoidable, and in 2014 Shopify introduced sharding partitioning the overall data and distributing merchants across shards by deterministic rules, since one merchant's orders never need to query another merchant's inventory anyway.

Sharding alone wasn't the end of the story. Shopify's own engineering blog, in a post titled "A Pods Architecture to Allow Shopify to Scale", describes the problem this created: by 2015 it was no longer possible to keep buying a bigger single database server, so the platform was forced to shard but throughout the codebase sat patterns like Sharding.with_each_shard do some_action end, meaning that if even one shard went down, that entire action became unavailable across the whole platform. As the number of shards kept growing, that risk kept growing right along with it. In 2016, the team sat down to reorganize Shopify's runtime architecture entirely around a new idea: pods. A pod not to be confused with a Kubernetes pod is a fully isolated set of datastores serving a specific group of shops, complete with its own MySQL shard, Redis node, and Memcached cluster. Pods do share some resources, like job workers, app servers, and load balancers, but those shared resources are only ever allowed to talk to one pod at a time; no action is permitted to reach across pod boundaries.

An apartment building where, decades ago, a single burst pipe in the basement could flood every unit on every floor at once, until the building was rewired so each unit sits on its own fully separate plumbing, electrical, and water line a burst pipe in unit 4B is now 4B's problem alone, and everyone else in the building never even notices it happened. That's the entire premise behind pods: instead of one shared system that can take the whole platform down when it breaks, you get thousands of small, disposable systems, and a single failure just quietly stays where it started.

Before pods (2014-2015):
  One giant database → sharded, but still one shared runtime
  → A single shard failure can make an action fail platform-wide
 
After pods (2016 onward):
  Many isolated pods, each with its own MySQL shard, Redis, Memcached
  → Shared resources (load balancers, job workers) talk to ONE pod at a time
  → A failure inside one pod stays inside that pod
Diagram contrasting a single shared database with isolated pod-based datastores

Moving Shops Between Pods Without Anyone Noticing

Pods solve isolation, but they create a new, quieter problem: over time, some pods end up hosting far busier shops than others, and the platform needs a way to physically move a shop from an overloaded pod to a calmer one without ever taking that shop offline. A post on Shopify's engineering blog walks through exactly this, describing how a web request for a shop is processed by a load balancer that consults a routing table and forwards the request to the correct pod, and then details the end-to-end process of physically relocating a shop's data from one pod to another at terabyte scale, entirely without downtime.

Moving apartments while somehow keeping every faucet running and every light switch working the entire time boxes get physically carried from the old address to the new one, but nobody living there ever has to go without electricity or water for even a second while it happens. Shard balancing is that same trick, applied to a merchant's live order history, inventory, and customer data, mid-flight, with real money moving through the store the whole time.

Skipping Microservices on Purpose

By the mid-2010s, most large engineering organizations facing this kind of scaling pain reached for the same playbook: break the monolith apart into microservices. Shopify's own engineering blog, in "Deconstructing the Monolith: Designing Software that Maximizes Developer Productivity", explains why the team deliberately chose not to take that path. Shopify is one of the largest Ruby on Rails codebases in existence, built by more than a thousand developers over more than a decade, encapsulating everything from billing merchants to managing third-party apps to handling shipping, all originally built into a single codebase with no boundaries between any of it. Eventually the downsides of that monolith started outweighing the benefits, so a survey went out to every developer working in the core system specifically to identify real pain points with data, rather than acting on anecdote alone. In early 2017, a small team was formed under the project name "Break-Core-Up-Into-Multiple-Pieces," which eventually became known internally as Componentization.

Rather than splintering into microservices, the team landed on what it calls a modular monolith: all the code still powers a single application and deploys together, but strict, enforced boundaries now separate distinct business domains from one another. A later follow-up post, "Under Deconstruction: The State of Shopify's Monolith", reveals just how large this codebase has grown Shopify's core monolith sits at over 2.8 million lines of Ruby code and 500,000 commits and describes the tooling built to keep those internal boundaries honest, including a static analysis tool called Packwerk that checks constant references across the codebase specifically to catch components quietly reaching into each other's internals.

A large university campus that never split itself into dozens of entirely separate, independently-run mini-schools each with its own gates, its own security, its own administration but did firmly assign every department to its own dedicated building, with real walls and real access controls between them, all while every student still shares one library card and one single enrollment system. You get most of the organizational clarity of separation, without needing to stand up and separately operate an entire duplicate university for every department. That's roughly the trade the modular monolith is making: real internal boundaries, real independence for individual teams, but still one single thing to test, deploy, and reason about as a whole.

Microservices approach (what Shopify considered and rejected):
  Break into many separately-deployed services
  → Gains: independent deployability
  → Costs: distributed systems complexity, network calls, duplicated auth, ops overhead
 
Shopify's modular monolith:
  One codebase, one deploy pipeline
  → Enforced component boundaries (checked by Packwerk)
  → Gains: most of microservices' organizational clarity
  → Without inheriting all of microservices' operational cost
Diagram of Shopify's modular monolith with enforced component boundaries

Teaching the Load Balancer to Run Its Own Checkout Queue

This is one of Shopify's oldest and most genuinely instructive engineering stories, and it comes straight from the company's own blog. In "Surviving Flashes of High-Write Traffic Using Scriptable Load Balancers", the post traces the problem back to Shopify's very first flash sale in 2007, right after the Colts beat the Bears in the Super Bowl, when a rush of fans buying celebratory t-shirts gave the team its first real taste of what happens when a huge crowd converges on one store in a very short window. The core difficulty wasn't really about read traffic at all: checkout, by its nature, is a flood of writes, and at the time every checkout session created its own new record in MySQL, with every single step of the checkout flow then modifying that same record again. A full rewrite of the checkout flow to reduce all that to a single write would have been the ideal fix, but wasn't something the team could ship before the very next flash sale was scheduled to hit.

The fix that shipped instead lived at the edge, not in the application. An O'Reilly interview with Shopify production engineer Emil Stolarsky, "The Power of Scriptable Load Balancers", explains the underlying tool: a scriptable load balancer is simply a load balancer with an embedded scripting engine, and Shopify built on OpenResty, which combines Nginx with LuaJIT, exposing APIs that let engineers actually modify and control the flow of requests as they pass through the load balancer itself. Using this, Shopify added a stateless queueing layer directly in front of checkout: buyers were shown a waiting-room page, injected with a small JavaScript snippet that continuously polled the checkout endpoint, and were only released through to actually pay once real capacity opened up.

The second post in the same series, Part II, describes a genuinely subtle refinement the team had to make after the first version shipped: the initial throttle didn't account for fairness, so a shopper who'd been patiently waiting from the very start of the sale could still lose their spot to someone who showed up later. The fix was to stamp each shopper's first checkout attempt with a securely signed, tamper-proof timestamp cookie, then compare that timestamp against everyone else currently in the queue to work out real priority, entirely without the load balancer needing to keep any shared state of its own.

A crowded deli counter's "now serving" ticket system, except reinvented so that no single central ticket machine exists at all every customer's own ticket somehow already encodes the exact moment they walked in, and any counter in the shop can independently work out who's actually next just by comparing tickets, with no need to phone a back office to check. That's what makes a stateless, timestamp-based queue elegant: fairness gets enforced without ever needing a central system to track who's currently waiting.

A flash sale begins, checkout demand exceeds capacity:
  Buyer clicks "Buy Now"
    → Scriptable load balancer (OpenResty: Nginx + LuaJIT) intercepts the request
    → Buyer receives a signed, timestamped queue-position cookie
    → Buyer is shown a waiting-room page, polling for their turn
  Capacity opens up
    → Load balancer compares timestamps across everyone currently queued
    → Earliest legitimate timestamps get released through to checkout first
Diagram of a stateless checkout queue enforced by scriptable load balancers

Rehearsing the Disaster Before It Happens

None of this matters if it's untested. A recent post on Shopify's engineering blog, "How We Prepare Shopify for BFCM", describes the load testing tool built specifically for this: Genghis, which runs scripted workflows simulating real buyer behaviour browsing, adding to cart, checking out and gradually ramps traffic against production infrastructure itself, running simultaneously from three separate Google Cloud regions to mimic genuinely global traffic patterns, while deliberately injecting flash-sale-style bursts on top of that baseline load to find the platform's actual breaking points ahead of time. The same post mentions Toxiproxy, an open-source tool Shopify built in-house specifically to simulate network failures and partitions, so teams can watch how the system behaves when services simply can't reach each other, rather than finding out for the first time live on Black Friday.

A fire department that runs full, realistic fire drills inside an actual occupied building months before fire season, deliberately setting off smoke machines and blocking random exits to see exactly where people get confused rather than waiting to discover the building's actual weak points during a real fire, when it's already too late to fix them. Load testing at this scale is Shopify's version of that same drill, run against its own live production infrastructure, months ahead of the one weekend a year when none of it is allowed to fail.

A Fair Note on What's Publicly Known Here

It's worth being upfront about the shape of this article: Shopify is unusually generous with public engineering detail compared to most large commerce platforms, which makes most of what's written here traceable directly back to first-party posts on shopify.engineering rather than outside speculation. Even so, some of the connective detail here specifically how today's Kafka event backbone, Vitess-based sharding experiments, and pod-balancing systems interact in full production, moment to moment is pieced together from a mix of official posts written at different points over roughly a decade, alongside independent technical breakdowns, rather than from one single, fully current, end-to-end architecture document. The core mechanics described here pods, the modular monolith, and scriptable load balancers rest on genuinely solid, first-party ground.

Putting It Together

None of the individual pieces here are exotic on their own. Database sharding, isolated tenancy, modular codebases, and edge-level request throttling are all recognizable patterns across the wider industry. What makes Shopify's story genuinely interesting is how consistently the platform chose isolation and boundaries over raw distribution pods isolate failure at the data layer instead of chasing ever-larger single databases, the modular monolith isolates team boundaries without paying the full operational cost of microservices, and scriptable load balancers isolate the fragile parts of checkout at the edge, entirely outside the application, so a flash sale that would have taken down the whole platform in 2007 barely registers as a blip by comparison today.

What This Means Going Forward

As Shopify keeps pushing further into new surfaces point-of-sale hardware, B2B commerce, and increasingly its own payments and shipping infrastructure the same underlying tension keeps resurfacing in a new shape: a pod model built around isolating individual online shops is a genuinely different problem than isolating, say, a single merchant's in-person and online sales in real time against each other, or a shared payments ledger that by definition can't be fully sharded apart the way independent shops can. The lesson underneath Shopify's story isn't really about MySQL, Nginx, or Packwerk specifically. It's a much older idea in a very Rails-shaped costume: the biggest engineering risk at scale usually isn't that something will fail it's that when it does, it takes everything else down along with it, so the real architectural win is very often just deciding, deliberately, what's allowed to stay broken alone.

Discussion