T-Ruby: Adding Static Typing to Ruby Without Runtime Overhead

Published: (February 24, 2026 at 04:50 PM EST)
4 min read
Source: Dev.to

Source: Dev.to

Cover image for T‑Ruby: Adding Static Typing to Ruby Without Runtime Overhead

Introduction

Static typing is a formidable tool that brings immense value to codebases of all sizes. From tiny scripts to massive monoliths, the benefits are hard to ignore: you get live documentation that is always up to date, enhanced readability, and a reliable safety net that significantly boosts code reliability.

Different dynamic languages have taken various paths to solve the typing puzzle. Python introduced native but optional type hints, while the JavaScript ecosystem moved toward a separate language entirely with TypeScript. Within the Ruby community, we’ve seen two distinct implementations in RBS and Sorbet, which have recently begun to converge thanks to the introduction of RBS inline and Sorbet’s support for it.

However, the current Ruby approach isn’t without friction. For many developers, typing still feels like a matter of personal preference rather than a core requirement. Because type checking is often seen as “extra,” it is far too easy for static checks to be ignored or forgotten entirely.

What Is T‑Ruby?

A groundbreaking experiment gaining traction is T‑Ruby. Unlike standard runtime type‑checking systems, T‑Ruby compiles typed code down to pure, undecorated Ruby with zero runtime overhead. In short, T‑Ruby is essentially TypeScript for the Ruby language: you write RBS‑inspired code that is eventually compiled into plain, standard Ruby.

While some might suggest using Crystal, there is a fundamental difference in philosophy. Crystal is a separate, Ruby‑like language with its own ecosystem. T‑Ruby, by contrast, is an extra layer designed to stay close to the Ruby we already know. Much like how TypeScript eventually outputs plain JavaScript, T‑Ruby ensures your final product remains pure Ruby.

Example: Typed HTTP Client

Below is a simple HTTP client that fetches the latest Ruby release tag from GitHub. With T‑Ruby, types are embedded directly into the class structure.

require "httparty"
require "json"
require "time"

class RubyVersion
  API_URL = "https://api.github.com/repos/ruby/ruby/releases/latest"

  class Response
    attr_reader :code: Integer
    attr_reader :json: Hash

    def initialize(code: Integer, json: Hash): nil
      @code = code
      @json = json
    end

    def success?: Boolean
      (200..299).include?(@code)
    end
  end

  def fetch_response: Response
    http = HTTParty.get(API_URL, headers: { 'User-Agent' => 'static-typing-demo' })
    Response.new(http.code.to_i, JSON.parse(http.body))
  end

  def self.fetch(printer: Proc<[String, String, Time], nil]): nil
    resp = new.fetch_response
    raise "HTTP #{resp.code}" unless (200..299).include?(resp.code)
    data = resp.json
    published = Time.parse((data['published_at'] || Time.now.utc.iso8601).to_s)
    printer.call((data['tag_name'] || data['name']).to_s, data['html_url'], published)
  end
end

By adding these types, we can strictly define the structure of the Proc and ensure the data passed through the system is valid. In standard Ruby, we would normally rely on documentation or manual guard clauses to achieve this level of certainty.

Setting Up T‑Ruby

  1. Install the gem:

    gem install t-ruby
  2. Initialize a configuration file:

    trc --init

The generated config lets you specify source folders for *.trb files, the destination for compiled Ruby, and post‑compile commands such as auto‑running RSpec or Minitest.

Rails Integration (Experimental)

Rails relies on a specific folder structure, so the cleanest approach is to store the entire app folder within your T‑Ruby source directory. Files that don’t contain T‑Ruby syntax are simply copied over to the destination folder unmodified. This mirrors the /src / /dist workflow common in modern JavaScript frameworks like Next.js.

Current Status and Outlook

T‑Ruby is still in a technical preview phase. The website and documentation look polished, but the implementation can be temperamental—official examples may fail immediately after installation.

Despite these rough edges, the potential is undeniable. Working on a small Rails app with T‑Ruby eliminated the need for a separate collection of RBS files. Writing types alongside business logic feels natural, and knowing the code is “type‑correct” at compile time provides a unique peace of mind. It’s a bold step forward for the Ruby ecosystem, even if it isn’t quite ready for production environments just yet.

Originally posted in my blog.

0 views
Back to Blog

Related posts

Read more »

Small Thing, Big Impact

!Trailing comma illustrationhttps://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s...

Steel Bank Common Lisp

About Steel Bank Common Lisp SBCL is a high performance Common Lisp compiler. It is open source / free software, with a permissive license. In addition to the...

Simplifying timestamp toggles in Rails

Overview I often use timestamps, like completed_at, as a boolean flag. It offers a bit more meta data than a real boolean, but on the UI you typically want to...

Stop Using .any? the Wrong Way in Rails

Introduction A single block passed to .any? can silently load thousands of records into memory—no warnings, no errors, just unnecessary objects. Most Rails dev...