This episode introduces type systems for Ruby: writing RBS signatures in .rbs files and checking them with Steep, plus Sorbet with gradual typing, sorbet-typed, and runtime integration for more reliable code in production.

Ruby is a dynamically typed language, but in large projects, a typo in a method name or a wrong argument type is only caught at runtime. Episode 18 introduces type systems you can layer on top of Ruby: RBS with the static type checker Steep, and Sorbet with gradual typing.
Neither approach forces you to annotate types everywhere at once. You start from critical files or methods, and the checker gradually turns on. The result: fewer bugs in production and automatically readable type documentation. You choose one tool based on your needs, not both at the same time.
Types are not a replacement for tests — the two catch different categories of bugs. Types check data contracts statically, while tests verify actual behavior. Use both side by side for the best results.
RBS is the official type description language from the Ruby team. Signatures are written in .rbs files separate from the code — describing methods, arguments, and return values:
class Kalkulator
def tambah: (Integer a, Integer b) -> Integer
def bagi: (Integer a, Integer b) -> Float | nil
attr_accessor nama: String
enddef tambah: (Integer a, Integer b) -> Integer declares that the tambah method takes two Integers and returns an Integer. Float | nil marks a union type — the method can return a Float or nil. RBS also supports generics, interfaces, and untyped for a transitional period. Signature files are typically placed in the sig/ directory, structured to mirror their classes.
You don't have to write signatures yourself. Many gems ship .rbs files inside their packages, and rbs collection helps gather them all in one place:
rbs collection init
rbs collection installrbs collection init creates the collection configuration file, then rbs collection install downloads the available gem signatures. This way the type checker understands third-party code without you retyping thousands of lines of signatures.
To speed things up, rbs prototype rb can generate an initial signature from existing Ruby code, and rbs validate checks the correctness of signature file syntax. These two commands are invaluable when adopting RBS in an already-running codebase.
Steep checks code against RBS signatures without running the program. Install and run it from the terminal:
gem install steep
steep init
steep checksteep check reads the signatures in sig/ and reports mismatches: arguments of the wrong type, returns that don't match the contract, or calls to methods that don't exist. The checker is configured through a Steepfile — specify the code path, signature path, and the libraries in use. Run Steep in CI so type regressions are caught before they break production.
Steep also works in the editor through the Language Server Protocol. With this setup, type feedback appears as you type, instead of waiting for the checker's terminal output. Combining editor and CI makes typing a natural part of the workflow.
Sorbet from Stripe works differently: types are written directly inside the Ruby file via a sigil comment on the first line and annotations:
# typed: true
class Kalkulator
extend T::Sig
sig { params(a: Integer, b: Integer).returns(Integer) }
def tambah(a, b)
a + b
end
end# typed: true enables type checking for that file. sig { params(...).returns(...) } declares the method contract. Sorbet also supports the strict and strong levels for files that are fully typed, so adoption can happen file by file without rewriting the whole codebase at once.
The strict level requires every method to have a signature, while strong tightens type enforcement overall. Teams usually raise the level gradually as code gets annotated, starting from false, then true, strict, and finally strong.
Sorbet provides the sorbet-typed repository containing signatures for popular gems, so the checker can understand third-party code. Sorbet also runs at runtime: if a signature is violated in production, an exception is raised according to policy. Set up a project with these commands:
bundle add sorbet sorbet-runtime
srb init
srb rbi sorbet-typed
srb tcsrb init creates the configuration skeleton and RBI files. srb rbi sorbet-typed imports signatures from the sorbet-typed repository, while srb tc runs type checking. For runtime, the library is available as sorbet-runtime. Sorbet offers an attractive compromise: type rigor when you need it, without changing Ruby itself.
Tip
Choose the tool that fits your needs: RBS is the official standard and more gems provide signatures for it; Sorbet excels at gradual adoption in large codebases with built-in runtime checking.
Key takeaways:
.rbs files.def nama: (Tipe a) -> Tipe declares arguments and return.rbs collection install gathers gem signatures automatically.# typed: sigil and sig annotations.sorbet-typed brings signatures for popular gems into the checker.In episode 19 we open Ruby's hidden door: metaprogramming with send, define_method, method_missing, and class_eval to build DSLs, then understand the singleton class and the method lookup chain. With types secure, we explore the magic of Ruby's dynamism.