From Zero to Typed: Shopify's Sorbet Rollout
Shopify's main monolith is large and changes constantly: roughly 400 commits land every day, and new versions deploy 40 times daily. The codebase spans 37,000 Ruby files, 622,000 methods, and over 2 million calls. With a dynamic language at that scale, even 150,000 tests and rigorous review can't fully guarantee stability of the monolith for merchants. Static typing shortens the feedback loop for developers, so the Ruby Infrastructure team set out to bring Sorbet to the entire codebase.
Adoption began in 2019. Today Sorbet runs on every pull request in CI, failing builds when type errors appear. All files must be at least typed: false, and 80% of files—tests included—are typed: true or stricter. Almost half of method calls are typed, and half of all methods carry signatures. Beyond the monolith, 60 internal projects employ Sorbet as well.
Tooling to Close the Gap
Sorbet cannot understand every Ruby construct, and Rails plus Shopify's many gems contribute their own idioms. The team built and open-sourced several tools to bridge that gap.
RuboCop Sorbet
Gradual typing still required sweeping changes to eliminate Ruby constructs Sorbet didn't recognize—non-constant superclasses, for instance, or metaprogramming via const_get. The RuboCop Sorbet rule suite forbids such patterns and can auto-correct them to Sorbet-compatible alternatives. It also enforces a minimal typed level on every file and conventions for writing signatures.
Tapioca for RBI Generation
For Sorbet to understand required gems, Shopify had two choices: pass whole gem source to Sorbet (slow and demanding full gem compatibility) or use Ruby Interface (RBI) files as lightweight representations. Predating Sorbet's own srb tooling, the team released Tapioca, which automatically generates accurate RBI files per gem. It loads all gems declared in the Gemfile, introspects their runtime types, then writes versioned RBI files for statically and most runtime-defined types.
That covered gems, but not Shopify's own domain-specific languages. Rails DSLs like belongs_to generate dozens of methods at runtime that are invisible to Sorbet statically. Tapioca now performs the same runtime introspection on application code to emit RBIs for these generated methods. Coverage includes Active Record associations, columns, enums, scopes, and typed stores, plus Action Mailer, Active Resource, controller helpers, Active Support current attributes, Rails URL helpers, FrozenRecord, IdentityCache, Google Protobuf definitions, SmartProperties, StateMachines, and more.
Spoom
As adoption grew, Shopify needed tooling built on Sorbet itself. Sorbet parses the monolith into an AST in seconds—far faster than the Whitequark parser—which makes it attractive for linters and static analyzers. Its Language Server Protocol mode (--lsp) also beats the --print JSON output, which runs to gigabytes for Shopify's monolith. Asking LSP questions returns answers in milliseconds.
Spoom wraps these capabilities for external tools: parsing Sorbet configuration, listing type-checked files, collecting metrics, bumping files to higher strictness levels, and connecting to Sorbet's LSP via Ruby API. It powers the SorbetMetrics dashboard used for coverage reporting.
Measuring Typing's Impact
The team subscribes to the Build-Measure-Learn loop and prefers empirical data to intuition. The monolith itself makes such measurement hard: its pace of change conflates new-feature errors with typing's effects, and its hundreds of gem dependencies can inject errors on any update.
Instead, they targeted an internal developer tool called dev. It is mature, changes slowly, and is maintained by few people. It has no external dependencies and little metaprogramming, yet sees heavy production use: thousands of runs daily across hundreds of machines, eliminating edge cases as a confound.
Shopify monitored all errors dev raised in production, categorized them by root cause, and evaluated whether typing could have prevented each one.
Ignoring Files Accumulates Debt
The first lesson: avoid typed: ignore. Ignoring a file can cause errors in files that reference constants defined within it. Sorbet does not even parse ignored files, so a constant's origin becomes unknown to it. The more files are ignored, the harder every other developer's typing work becomes. Shopify's rule of thumb is typed: true for application files and typed: false for tests. It reserves typed: ignore for difficult test files or specific cases like Protobuf definition files, whose DSL handling is delegated to Tapioca RBIs.
Even typed: false Catches Real Bugs
At typed: false, Sorbet validates constant resolution across the codebase. Enabling it unearthed mistakes like StandardException in place of StandardError and NotImplemented versus NotImplementedError, along with dead code that referenced constants deleted months prior. Interestingly, most NameErrors surfaced not in main execution paths, which tests covered, but in error-handling code paths.
Moving all of dev to typed: false—without adding any signatures—eradicated production NameErrors as of October 2019.
The pattern held across multiple projects: enabling Sorbet eliminates NameErrors caused by developer mistakes. It does not stop those triggered through metaprogramming such as const_get. Sorbet's stricter constant resolution does add constraints, but that strictness itself helps developers spot problems earlier.
Typing Hotspots Pays Off
After the initial rollout, the next experiment targeted the parts of the dev application that get the most reuse. The team moved a subset of typed: false files to typed: true, concentrating on the helpers module (the blue dots in the figure below).

Files strictnesses in dev (the colored dots are the helpers)
Typing roughly 20% of the application's files — still without any method signatures — produced a measurable drop in NoMethodErrors in production for those typed: true files.

NoMethodErrors in production for dev (in red the errors raised from the helpers)
Those results suggested stricter typing could catch more than just type mismatches. The team is now adding signatures to the typed: true files in dev to measure the effect on TypeErrors and ArgumentErrors in production.
Keeping Pace with Ruby
The monolith tracks Ruby trunk closely, which means moving to Ruby 2.7 months ago. That upgrade required substantial changes in Sorbet to support new syntax, including beginless ranges and numbered parameters, plus behavior changes like forbidding circular argument references. Work continues on the forwarding arguments syntax and pattern matching. Stripe is separately making keyword arguments compatible with Ruby 2.7 behavior.
The Push to 100% typed: true
The next milestone is moving every file in the monolith to at least typed: true, and making that the default for all new files. Reaching that goal means making Sorbet and the surrounding tooling handle the remaining Ruby idioms that can't be type checked yet.
Current work focuses on Rails-specific gaps: Sorbet support for ActiveSupport::Concerns (pull requests #3424, #3468, #3486) and a solution for inclusion requirements. Tapioca is also being improved for better RBI generation for generics and GraphQL support.
Typing Beyond Sorbet
Shopify doesn't treat Sorbet as the final word on Ruby type checking. Ruby 3 will ship with RBS, a different approach to static typing. RBS isn't mature enough for Shopify's needs yet — mainly due to speed and other limitations outlined by Stripe — but the team is already collaborating with Ruby core developers, academics and Stripe to improve its specification.
Shopify has open-sourced RBS parser, a C++ parser capable of translating a subset of RBS to RBI, and is working on making RBS partially compatible with Sorbet.
The Ruby Infrastructure team sees typing — whatever the specific solution — as a clear win for Ruby, Shopify and its merchants. The plan is to keep investing, both in Sorbet and in the broader typing ecosystem. As adoption gets easier, especially on Rails projects, gradual typing will become mandatory on more internal projects. The long-term goal is to bring Ruby closer to compiled, statically typed languages in terms of safety, speed and tooling.



