【WIP】RailsでSorbetとTapiocaを導入してみた

## 本記事作成に至った経緯(Background)
アイスとタピオカ、響きからして全人類が好だと思います！！！

 ## 前提
みなさん型を意識して開発してますか。型好きですよね。え、Rubyには型がないって？？

>文句言われるわけですよね。
>Ruby is Dead, Rails is Dead. Because We don't have static typing. — 発表スライド
>Rubyは死んだとかRailsはもう終わりだー。なんかガッカリすることもあるんですけど、そういう方は2010年代最近話題になっているホットになっている言語はみんな静的型付け言語持っているのにRubyにはそれがない。おじいちゃんだから— Matz
>【引用: [[JA][Keynote] Ruby3 Typing / Yukihiro "Matz" Matsumoto](https://www.youtube.com/watch?v=2Ag8l-wq5qk)】 

動画の通り2016年9月のカンファレンスでRubyと型には言及がありました。終盤に東京オリンピックまでに～と述べてましたが、しっかり2020年12月25日に3.0.0が正式リリースされましたね。
[Ruby 3.0.0 リリース](https://www.ruby-lang.org/ja/news/2020/12/25/ruby-3-0-0-released/)

>2010年代は静的型言語の時代でした。Rubyは抽象解釈を武器に、型宣言なしで静的型チェックする未来を目指します。RBSとTypeProfはその第一歩です。Rubyがもたらす誰も見たことがない静的型の世界を見守ってください — Matz

Ruby3.0.0で静的解析にRBSとTypeProfが導入されました。ここから最新RubyKaigi2025まで型周りの話が永遠とRuby界隈ではHotトピックなわけですが正直なところどうなんでしょう。と思い調査しようと思いました。
## 本題
### 他社事例
Ruby、Railsを使っている企業は山ほどありますが、他社はどうしているのか調べてみました。
・Eight from sansan: [EightではRubyへの型導入を進めています](https://buildersbox.corp-sansan.com/entry/eight-steep-vs-sorbet)
・Findy: [FindyのRailsプロジェクトでSorbetの型チェックを試してみた](https://tech.findy.co.jp/entry/2025/04/24/070000)
・Timee: [RBS に出会って変わった Ruby への向き合い方](https://tech.timee.co.jp/entry/2024/12/24/000000)
・WEAR form zozo: [rbs-traceを使ってWEARで型生成を試してみた](https://speakerdeck.com/oyamakei/tried-rbs-trace-on-wear)
### Sorbetについて
RubyKaigi2019でStripe社から発表があったRuby向け型チェッカーであるSorbet🍨の概要です。
[[EN] State of Sorbet: A Type Checker for Ruby](https://www.youtube.com/watch?v=odmlf_ezsBo)
スライド: https://sorbet.run/talks/RubyKaigi2019/#/
### Tapiocaについて
Sorbetが公式に推奨するRBI生成方法であるShopify社のTapiocaの概要です。
RubyKaigi2023の講演情報はこちら。
[[EN] Generating RBIs for dynamic mixins with Sorbet and Tapioca](https://www.youtube.com/watch?v=UpbVZ4Gqk3c&t=910s)
スライド:   [Generating RBIs for dynamic mixins with Sorbet and Tapioca](https://drive.google.com/file/d/1TI0XQPlOie33N8mzGcZ4JJfscD9iMld0/view)
リリース情報: [Tapioca is the recommended way to generate RBIs for Sorbet](https://sorbet.org/blog/2022/07/27/srb-tapioca)
## やってみよう
### 導入手順
#### 1. sorbetとtapiocaのインストール
```gemfile
gem 'sorbet', :group => :development
gem 'sorbet-runtime'
gem 'tapioca', require: false, group: [:development, :test]
```
公式: https://sorbet.org/
公式Github: https://github.com/sorbet/sorbet
公式Github: https://github.com/Shopify/tapioca
#### 2. インストール確認
```powershell
$ docker compose -f compose.yaml run --rm web bundle exec srb
[+] Creating 1/0
 ✔ Container rails_research-db-1  Running                                                         0.0s 
No sorbet/ directory found. Maybe you want to run 'srb init'?

A type checker for Ruby

Usage:
  srb                                 Same as "srb t"
  srb (init | initialize)             Initializes the `sorbet` directory
  srb rbi [options]                   Manage the `sorbet` directory
  srb (t | tc | typecheck) [options]  Typechecks the code

Options:
  -h, --help     View help for this subcommand.
  --version      Show version.

For full help:
  https://sorbet.org
```
sorbeのsrbコマンドが使用可能になった。
```powershell
$ docker compose -f compose.yaml run --rm web bundle exec srb typecheck -e 'puts "Hello, world!"'
[+] Creating 1/0
 ✔ Container rails_research-db-1  Running                                                         0.0s 
No errors! Great job.
```
sorbeのsrbコマンドオプションのtypecheckも使用可能。
```powershell
$ docker compose -f compose.yaml run --rm web bundle exec ruby -e 'puts(require "sorbet-runtime")'
[+] Creating 1/0
 ✔ Container rails_research-db-1  Running                                                         0.0s 
true
```
sorbet-runtimeはDockerコンテナ内の環境に正しくインストールされており利用可能。
```powershell
$ docker compose -f compose.yaml run --rm web bundle exec tapioca help
[+] Creating 1/0
 ✔ Container XXXXXX  Running                                                0.0s 
Commands:
  tapioca --version, -v      # Show version
  tapioca annotations        # Pull gem RBI annotations from remote sources
  tapioca check-shims        # Check duplicated definitions in shim RBIs
  tapioca configure          # Initialize folder structure and type checking configuration
  tapioca dsl [constant...]  # Generate RBIs for dynamic methods
  tapioca gem [gem...]       # Generate RBIs from gems
  tapioca help [COMMAND]     # Describe available commands or one specific command
  tapioca init               # Get project ready for type checking
  tapioca require            # Generate the list of files to be required by tapioca
  tapioca todo               # Generate the list of unresolved constants

Options:
  -c, [--config=<config file path>]                  # Path to the Tapioca configuration file
                                                     # Default: sorbet/tapioca/config.yml
  -V, [--verbose], [--no-verbose], [--skip-verbose]  # Verbose output for debugging purposes
                                                     # Default: false
```
tapiocaコマンドが使えるようになりました  
#### 3.  初期化コマンド実施
tapioca initコマンドでいざ初期化していきましょう。
```powershell
$ docker compose -f compose.yaml run --rm web bundle exec tapioca init
＜省略＞
.......................
.......................
.......................
Documentation
We recommend skimming these docs to get a feel for how to use Sorbet:
- Gradual Type Checking: https://sorbet.org/docs/gradual
- Enabling Static Checks: https://sorbet.org/docs/static
- RBI Files: https://sorbet.org/docs/rbi
```
自動生成の詳細の中身はこちら
<details>

```powershell
$ docker compose -f compose.yaml run --rm web bundle exec tapioca init
[+] Creating 1/0
 ✔ Container rails_research-db-1  Running                                                                                                                                                          0.0s 
      create  sorbet/config
      create  sorbet/tapioca/config.yml
      create  sorbet/tapioca/require.rb
      create  bin/tapioca
Retrieving index from central repository... Done
Listing gems from Gemfile.lock... Done
Removing annotations for gems that have been removed...  Nothing to do
Fetching gem annotations from central repository... 

  Fetched actionmailer
      create  sorbet/rbi/annotations/actionmailer.rbi

  Fetched actionpack
      create  sorbet/rbi/annotations/actionpack.rbi

  Fetched actionview
      create  sorbet/rbi/annotations/actionview.rbi

  Fetched activejob
      create  sorbet/rbi/annotations/activejob.rbi

  Fetched activemodel
      create  sorbet/rbi/annotations/activemodel.rbi

  Fetched activerecord
      create  sorbet/rbi/annotations/activerecord.rbi

  Fetched activesupport
      create  sorbet/rbi/annotations/activesupport.rbi

  Fetched globalid
      create  sorbet/rbi/annotations/globalid.rbi

  Fetched minitest
      create  sorbet/rbi/annotations/minitest.rbi

  Fetched railties
      create  sorbet/rbi/annotations/railties.rbi

  Fetched rainbow
      create  sorbet/rbi/annotations/rainbow.rbi

Done
Removing RBI files of gems that have been removed:

  Nothing to do.

Generating RBI files of gems that are added or updated:

Requiring all gems to prepare for compiling...  Done

  Compiled actionmailer
      create  sorbet/rbi/gems/actionmailer@8.0.2.rbi

  Compiled actiontext
      create  sorbet/rbi/gems/actiontext@8.0.2.rbi

  Compiled activejob
  Compiled actionmailbox
      create  sorbet/rbi/gems/actionmailbox@8.0.2.rbi

      create  sorbet/rbi/gems/activejob@8.0.2.rbi

  Compiled actioncable
      create  sorbet/rbi/gems/actioncable@8.0.2.rbi

  Compiled ast
      create  sorbet/rbi/gems/ast@2.4.3.rbi

  Compiled base64
      create  sorbet/rbi/gems/base64@0.2.0.rbi

  Compiled benchmark
      create  sorbet/rbi/gems/benchmark@0.4.0.rbi

  Compiled bigdecimal
      create  sorbet/rbi/gems/bigdecimal@3.1.9.rbi

  Compiled addressable
  Compiled bindex (empty output)
      create  sorbet/rbi/gems/bindex@0.8.1.rbi

      create  sorbet/rbi/gems/addressable@2.8.7.rbi

  Compiled activemodel
      create  sorbet/rbi/gems/activemodel@8.0.2.rbi

  Compiled builder (empty output)
      create  sorbet/rbi/gems/builder@3.3.0.rbi

  Compiled bootsnap
      create  sorbet/rbi/gems/bootsnap@1.18.4.rbi

  Compiled activestorage
      create  sorbet/rbi/gems/activestorage@8.0.2.rbi

  Compiled connection_pool (empty output)
      create  sorbet/rbi/gems/connection_pool@2.5.3.rbi

  Compiled crass
      create  sorbet/rbi/gems/crass@1.0.6.rbi

  Compiled date
      create  sorbet/rbi/gems/date@3.4.1.rbi

  Compiled dotenv
      create  sorbet/rbi/gems/dotenv@3.1.8.rbi

  Compiled actionview
      create  sorbet/rbi/gems/actionview@8.0.2.rbi

  Compiled drb
      create  sorbet/rbi/gems/drb@2.2.1.rbi

  Compiled erubi
      create  sorbet/rbi/gems/erubi@1.13.1.rbi

  Compiled et-orbi
      create  sorbet/rbi/gems/et-orbi@1.2.11.rbi

  Compiled globalid
      create  sorbet/rbi/gems/globalid@1.2.1.rbi

  Compiled fugit
      create  sorbet/rbi/gems/fugit@1.11.1.rbi

  Compiled importmap-rails
      create  sorbet/rbi/gems/importmap-rails@2.1.0.rbi

  Compiled io-console (empty output)
      create  sorbet/rbi/gems/io-console@0.8.0.rbi

  Compiled brakeman
      create  sorbet/rbi/gems/brakeman@7.0.2.rbi

  Compiled jbuilder
      create  sorbet/rbi/gems/jbuilder@2.13.0.rbi

  Compiled i18n
  Compiled concurrent-ruby
      create  sorbet/rbi/gems/i18n@1.14.7.rbi

      create  sorbet/rbi/gems/concurrent-ruby@1.3.5.rbi

  Compiled lint_roller (empty output)
      create  sorbet/rbi/gems/lint_roller@1.1.0.rbi

  Compiled json
      create  sorbet/rbi/gems/json@2.11.3.rbi

  Compiled logger
      create  sorbet/rbi/gems/logger@1.7.0.rbi

  Compiled loofah
      create  sorbet/rbi/gems/loofah@2.24.0.rbi

  Compiled actionpack
      create  sorbet/rbi/gems/actionpack@8.0.2.rbi

  Compiled mini_mime
      create  sorbet/rbi/gems/mini_mime@1.1.5.rbi

  Compiled activesupport
  Compiled marcel
      create  sorbet/rbi/gems/marcel@1.0.4.rbi

  Compiled matrix
      create  sorbet/rbi/gems/activesupport@8.0.2.rbi

      create  sorbet/rbi/gems/matrix@0.4.2.rbi

  Compiled msgpack
      create  sorbet/rbi/gems/msgpack@1.8.0.rbi

  Compiled mysql2
      create  sorbet/rbi/gems/mysql2@0.5.6.rbi

  Compiled capybara
      create  sorbet/rbi/gems/capybara@3.40.0.rbi

  Compiled net-pop
      create  sorbet/rbi/gems/net-pop@0.1.2.rbi

  Compiled net-protocol
      create  sorbet/rbi/gems/net-protocol@0.2.2.rbi

  Compiled minitest
      create  sorbet/rbi/gems/minitest@5.25.5.rbi

  Compiled netrc
      create  sorbet/rbi/gems/netrc@0.11.0.rbi

  Compiled nio4r
      create  sorbet/rbi/gems/nio4r@2.7.4.rbi

  Compiled parallel
      create  sorbet/rbi/gems/parallel@1.27.0.rbi

  Compiled net-smtp
      create  sorbet/rbi/gems/net-smtp@0.5.1.rbi

  Compiled prettyprint
      create  sorbet/rbi/gems/prettyprint@0.2.0.rbi

  Compiled pp
      create  sorbet/rbi/gems/pp@0.6.2.rbi

  Compiled propshaft
      create  sorbet/rbi/gems/propshaft@1.1.0.rbi

  Compiled language_server-protocol (empty output)
      create  sorbet/rbi/gems/language_server-protocol@3.17.0.4.rbi

  Compiled public_suffix
      create  sorbet/rbi/gems/public_suffix@6.0.2.rbi

  Compiled psych
      create  sorbet/rbi/gems/psych@5.2.5.rbi

  Compiled raabro
      create  sorbet/rbi/gems/raabro@1.4.0.rbi

  Compiled nokogiri
      create  sorbet/rbi/gems/nokogiri@1.18.8.rbi

  Compiled net-imap
  Compiled racc
      create  sorbet/rbi/gems/racc@1.8.1.rbi

      create  sorbet/rbi/gems/net-imap@0.5.8.rbi

  Compiled puma
      create  sorbet/rbi/gems/puma@6.6.0.rbi

  Compiled rack-session
      create  sorbet/rbi/gems/rack-session@2.1.1.rbi

  Compiled rack-test
  Compiled rails (empty output)
      create  sorbet/rbi/gems/rails@8.0.2.rbi

      create  sorbet/rbi/gems/rack-test@2.2.0.rbi

  Compiled rackup
      create  sorbet/rbi/gems/rackup@2.2.1.rbi

  Compiled rails-dom-testing
      create  sorbet/rbi/gems/rails-dom-testing@2.2.0.rbi

  Compiled rails-html-sanitizer
      create  sorbet/rbi/gems/rails-html-sanitizer@1.6.2.rbi

  Compiled rainbow (empty output)
      create  sorbet/rbi/gems/rainbow@3.1.1.rbi

  Compiled rack
      create  sorbet/rbi/gems/rack@3.1.14.rbi

  Compiled rake
      create  sorbet/rbi/gems/rake@13.2.1.rbi

  Compiled railties
      create  sorbet/rbi/gems/railties@8.0.2.rbi

  Compiled rbi
      create  sorbet/rbi/gems/rbi@0.3.3.rbi

  Compiled activerecord
  Compiled reline (empty output)
      create  sorbet/rbi/gems/reline@0.6.1.rbi

      create  sorbet/rbi/gems/activerecord@8.0.2.rbi

  Compiled rbs
      create  sorbet/rbi/gems/rbs@3.9.4.rbi

  Compiled regexp_parser
      create  sorbet/rbi/gems/regexp_parser@2.10.0.rbi

  Compiled rexml
      create  sorbet/rbi/gems/rexml@3.4.1.rbi

  Compiled rubocop-performance (empty output)
      create  sorbet/rbi/gems/rubocop-performance@1.25.0.rbi

  Compiled rubocop-rails-omakase (empty output)
      create  sorbet/rbi/gems/rubocop-rails-omakase@1.1.0.rbi

  Compiled rubocop-ast (empty output)
      create  sorbet/rbi/gems/rubocop-ast@1.44.1.rbi

  Compiled ruby-progressbar (empty output)
      create  sorbet/rbi/gems/ruby-progressbar@1.13.0.rbi

  Compiled securerandom
      create  sorbet/rbi/gems/securerandom@0.4.1.rbi

  Compiled rubyzip
      create  sorbet/rbi/gems/rubyzip@2.4.1.rbi

  Compiled solid_cable
      create  sorbet/rbi/gems/solid_cable@3.0.8.rbi

  Compiled rubocop-rails (empty output)
      create  sorbet/rbi/gems/rubocop-rails@2.31.0.rbi

  Compiled solid_cache
      create  sorbet/rbi/gems/solid_cache@1.0.7.rbi

  Compiled solid_queue
      create  sorbet/rbi/gems/solid_queue@1.1.5.rbi

  Compiled stimulus-rails
      create  sorbet/rbi/gems/stimulus-rails@1.3.4.rbi

  Compiled stringio (empty output)
      create  sorbet/rbi/gems/stringio@3.1.7.rbi

  Compiled selenium-webdriver
      create  sorbet/rbi/gems/selenium-webdriver@4.32.0.rbi

  Compiled spoom
      create  sorbet/rbi/gems/spoom@1.6.3.rbi

  Compiled timeout
      create  sorbet/rbi/gems/timeout@0.4.3.rbi

  Compiled thor
      create  sorbet/rbi/gems/thor@1.3.2.rbi

  Compiled turbo-rails
      create  sorbet/rbi/gems/turbo-rails@2.0.13.rbi

  Compiled unicode-display_width (empty output)
      create  sorbet/rbi/gems/unicode-display_width@3.1.4.rbi

  Compiled unicode-emoji (empty output)
      create  sorbet/rbi/gems/unicode-emoji@4.0.4.rbi

  Compiled tzinfo
      create  sorbet/rbi/gems/tzinfo@2.0.6.rbi

  Compiled uri
      create  sorbet/rbi/gems/uri@1.0.3.rbi

  Compiled rdoc
  Compiled useragent (empty output)
      create  sorbet/rbi/gems/useragent@0.16.11.rbi

      create  sorbet/rbi/gems/rdoc@6.13.1.rbi

  Compiled tapioca
      create  sorbet/rbi/gems/tapioca@0.16.11.rbi

  Compiled web-console
      create  sorbet/rbi/gems/web-console@4.2.1.rbi

  Compiled mail
  Compiled websocket-extensions
      create  sorbet/rbi/gems/websocket-extensions@0.1.5.rbi

      create  sorbet/rbi/gems/mail@2.8.1.rbi

  Compiled xpath
      create  sorbet/rbi/gems/xpath@3.2.0.rbi

  Compiled websocket-driver
      create  sorbet/rbi/gems/websocket-driver@0.7.7.rbi

  Compiled websocket
      create  sorbet/rbi/gems/websocket@1.2.11.rbi

  Compiled yard-sorbet
      create  sorbet/rbi/gems/yard-sorbet@0.9.0.rbi

  Compiled zeitwerk
      create  sorbet/rbi/gems/zeitwerk@2.7.2.rbi

  Compiled rubocop (empty output)
      create  sorbet/rbi/gems/rubocop@1.75.5.rbi

  Compiled yard
      create  sorbet/rbi/gems/yard@0.9.37.rbi

  Compiled prism
      create  sorbet/rbi/gems/prism@1.4.0.rbi
 
  Compiled parser
      create  sorbet/rbi/gems/parser@3.3.8.0.rbi


Checking generated RBI files...  Done

  Changed strictness of sorbet/rbi/gems/drb@2.2.1.rbi to `typed: false` (conflicting with DSL files)

  Changed strictness of sorbet/rbi/gems/json@2.11.3.rbi to `typed: false` (conflicting with DSL files)

  Changed strictness of sorbet/rbi/gems/logger@1.7.0.rbi to `typed: false` (conflicting with DSL files)

  Changed strictness of sorbet/rbi/gems/msgpack@1.8.0.rbi to `typed: false` (conflicting with DSL files)

  Changed strictness of sorbet/rbi/gems/net-imap@0.5.8.rbi to `typed: false` (conflicting with DSL files)

  Changed strictness of sorbet/rbi/gems/net-protocol@0.2.2.rbi to `typed: false` (conflicting with DSL files)

  Changed strictness of sorbet/rbi/gems/net-smtp@0.5.1.rbi to `typed: false` (conflicting with DSL files)

  Changed strictness of sorbet/rbi/gems/pp@0.6.2.rbi to `typed: false` (conflicting with DSL files)

  Changed strictness of sorbet/rbi/gems/rdoc@6.13.1.rbi to `typed: false` (conflicting with DSL files)

  Changed strictness of sorbet/rbi/gems/rexml@3.4.1.rbi to `typed: false` (conflicting with DSL files)

  Changed strictness of sorbet/rbi/gems/tapioca@0.16.11.rbi to `typed: false` (conflicting with DSL files)

All operations performed in working directory.
Please review changes and commit them.

Finding all unresolved constants, this may take a few seconds... Nothing to do
This project is now set up for use with Sorbet and Tapioca

The sorbet/ folder should exist and look something like this:

├── config             # Default options to be passed to Sorbet on every run
└── rbi/
  ├── annotations/     # Type definitions pulled from the rbi-central repository
  ├── gems/            # Autogenerated type definitions for your gems
  └── todo.rbi         # Constants which were still missing after RBI generation
└── tapioca/
  ├── config.yml       # Default options to be passed to Tapioca
  └── require.rb       # A file where you can make requires from gems that might be needed for gem RBI generation

Please check this folder into version control.

🤔 What's next

1. Many Ruby applications use metaprogramming DSLs to dynamically generate constants and methods.
  To generate type definitions for any DSLs in your application, run:

  bin/tapioca dsl

2. Check whether the constants in the sorbet/rbi/todo.rbi file actually exist in your project.
  It is possible that some of these constants are typos, and leaving them in todo.rbi will
  hide errors in your application. Ideally, you should be able to remove all definitions
  from this file and delete it.

3. Typecheck your project:

  bundle exec srb tc

  There should not be any typechecking errors.

4. Upgrade a file marked "# typed: false" to "# typed: true".
  Then, run: bundle exec srb tc and try to fix any errors.

  You can use Spoom to bump files for you:

  spoom bump --from false --to true

  To learn more about Spoom, visit: https://github.com/Shopify/spoom

5. Add signatures to your methods with sig. To learn how, read: https://sorbet.org/docs/sigs

Documentation
We recommend skimming these docs to get a feel for how to use Sorbet:
- Gradual Type Checking: https://sorbet.org/docs/gradual
- Enabling Static Checks: https://sorbet.org/docs/static
- RBI Files: https://sorbet.org/docs/rbi
```
</details>

 生成されたファイルは以下に格納されています
```powershell
$ git status
(use "git add <file>..." to include in what will be committed)
      bin/tapioca
      sorbet/
```

ここまでがインストール編になります。
to be continued...