Shopify’s journey to faster breadth first GraphQL execution (2026) Shopify
Justy and Cody discuss Shopify's new breadth-first GraphQL execution engine, 'Cardinal,' which claims up to 15x faster execution and 90% less memory for large, nested queries by resolving fields once across all objects instead of per-object.
Script: DeepSeek V4 Pro Voice: OpenAI TTS
Transcript
Justy Okay, so you know how GraphQL is supposed to let you ask for exactly what you need? Shopify just published this thing saying the real bottleneck isn't the database—it's the execution engine assembling the response. They built a whole new one called Cardinal.
Cody Right. And that checks out from a profiling standpoint. If you're asking for two hundred and fifty products, each with two hundred and fifty variants, the default depth-first resolver is going to visit every single field on every single object, one at a time. It's a ton of CPU work that doesn't get amortized.
Justy That's the claim—fifteen times faster execution, ninety percent less memory for big list queries. They were shaving seconds off P50 times. Which, if you're a merchant waiting for your admin to load, that's NOT a backend curiosity.
Cody Mm-hm.
Cody The hidden cost they point out is really about linear scale. In a depth-first traversal, you descend into a product, resolve all its variants, and only then move to the next product. The graphql-ruby gem, which they've used since twenty fifteen, follows the spec's reference implementation in JS. It just doesn't batch field resolution across objects by default.
Justy So instead of running the same field resolver sixty-two thousand times, Cardinal flips the model. It resolves one field across every object in the list at once. Breadth-first, not depth-first.
Cody Exactly. And that's where the memory savings come from too, because you're not holding all those intermediate resolver states in a deep call stack. But I'm curious how they handled the migration. You can't just flip a switch on a production GraphQL schema and expect all your custom resolvers to work breadth-first. That usually means rewriting a lot of execution logic.
Justy I was WAITING for you to go there. Yes, they had to rebuild the engine. But here's the product angle: who actually needs this? It's not your average SaaS with a few nested comments. This is for platforms where the fan-out is geometric. Marketplaces, e-commerce with deep variant trees, anything where a list-of-lists query can explode into hundreds of thousands of node resolutions.
Cody Right, right. And they're running this in Ruby, which isn't always the first language you think of for high-throughput execution engines. The fact that they got a fifteen-x speedup by changing the traversal algorithm, not the language, is the interesting part. It suggests the old model was fundamentally broken at scale, not just slow.
Justy It's a classic case of a spec that optimized for ergonomics and then hit a wall at massive breadth. They even call it an 'open letter to the GraphQL community,' which is a little dramatic, but I get it. If you're maintaining a fork of graphql-ruby to make this work, you want the pattern to spread.
Cody I'd want to see their benchmarks for queries that aren't just massive fan-out lists though. If your query is shallow and wide, breadth-first is a clear win. But if you're deeply nested with a lot of conditional logic, the overhead of batch scheduling could get weird. They don't really get into that trade-off in this post.
Justy That's fair. But for their actual use case—merchants loading huge product catalogs—it's a no-brainer. It's one of those things where the academic 'it depends' is less important than the production reality of seconds shaved off page loads.
Cody Yeah. And I respect that they built it in the open on top of their existing gem. It's not a new GraphQL server from scratch; it's a new execution layer. That's a pragmatic engineering story, even if the migration is a beast.
Justy Totally. Anyway, I'm just glad I'm not the one refactoring a decade-old resolver graph. I spent my morning fighting with a busted CI pipeline and that felt heroic enough.
Cody A busted pipeline is the universe's way of keeping you humble. I tried to reorganize my desk and ended up just making a bigger mess. So I'm living vicariously through Shopify's clean engine rewrite.
Justy I feel that. Okay, for anyone digging into this, they built it as a replacement for the graphql-ruby execution backend. If you're on that gem and dealing with huge lists, it's probably worth a look. If you're on something else, the pattern is the real takeaway.
Cody Agreed. And if the GraphQL spec ever formally adopts a batching model like this, it'll be because of these kinds of production scars. Until then, it's a fascinating fork.
Justy That is such an Exploring Next take. I cannot believe this is how we're spending a Wednesday.