Ruby давно отримала визнання завдяки своїй гнучкості та виразності, ставши улюбленим інструментом серед розробників, які цінують швидке створення прототипів і зручність використання. Однак ця динамічна природа також принесла певні труднощі, особливо при роботі з великими кодовими базами, де баги та непогодження можуть залишатися непоміченими. З часом спільнота Ruby зрозуміла необхідність у інструментах, які забезпечать більшу передбачуваність та безпеку коду Ruby без втрати його динамічної суті. Ось так і з’явилася статична типізація.
Stripe була першою, хто запровадив перевірку типів через свій гем Sorbet. Згодом команда Ruby офіційно підхопила цю тему. З того часу існує два доступних рішення для статичної типізації. Обидва є чудовими, кожне з них має свої переваги та недоліки.
Сьогодні я не буду обговорювати Sorbet — чудовий інструмент, який використовують багато компаній, включаючи Shopify. Замість цього я надам огляд офіційного рішення Ruby на 2025 рік.
RBS, або Ruby Signature, є основою системи статичної типізації Ruby. Він дозволяє розробникам визначати підписи типів для свого коду Ruby без зміни його поведінки під час виконання. Надаючи формальний спосіб описувати структуру коду Ruby, RBS допомагає розробникам:
- Виявляти помилки типів на ранніх етапах розробки.
- Покращувати документацію з чіткими анотаціями типів.
За останні роки RBS зазнав численних покращень, включаючи кращу підтримку складних структур даних, узагальнень та більш глибоку інтеграцію з основними бібліотеками Ruby. Ця еволюція зробила його незамінним інструментом для розробників, які прагнуть додати більшої строгості до своїх проектів на Ruby.
Хоча RBS надає основу для статичної типізації, для фактичної перевірки типів потрібен інший інструмент — Steep. Розроблений як супутній інструмент для RBS, Steep аналізує код Ruby згідно з його підписами типів, щоб виявляти можливі невідповідності типів та інші проблеми.
Steep дозволяє розробникам Ruby:
- Поступово впроваджувати статичну типізацію в існуючі кодові бази.
- Забезпечувати узгодженість між підписами типів та фактичним кодом.
- Виявляти тонкі баги, які можуть залишитися непоміченими.
Інтеграція з CI/CD pipeline дозволила командам легко впроваджувати стандарти безпеки типів, що сприяє загальній надійності та підтримуваності їхніх кодових баз.
Підсумовуючи, для правильної перевірки статичних типів в Ruby нам потрібно два компоненти: RBS
і Steep
. Це достатньо, щоб почати, хоча можна також отримати переваги від сучасних IDE, таких як Visual Studio Code (з додатковими розширеннями) або RubyMine (яка підтримує RBS прямо з коробки).
Особисто я працюю з Ruby більше десяти років і завжди відчував брак чогось, що могло б краще описувати код, ніж мої тестові набори. RBS нарешті може це забезпечити. Він допомагає не лише перевіряти, що, скажімо, параметр age
, переданий у метод, має бути типу Integer
, але й служить чудовою документацією, яка завжди актуальна.
Занурення в RBS та перевірку типів вимагає зміни мислення. Від "Давайте спочатку напишемо швидкий брудний скрипт і будемо покращувати його пізніше, якщо це буде можливим" до "Напишемо один раз, і це буде працювати завжди". Не буквально, але дуже близько до цього. Це відчуття схоже на впровадження TDD або Rubocop (коли він тільки з'явився на ринку). Треба думати трохи по-іншому і прийняти, що на початку процес буде йти повільно.
Коли ви звикнете до синтаксису RBS, екосистеми, з усіма принципами, що з ним пов'язані, ви будете здивовані, як швидко почнете рухатися. Це таке чудове відчуття впевненості у своєму коді. Таке, якого не отримаєш, покриваючи кожну частину коду тестами. Додайте підтримку IDE, і ви більше не захочете повертатись назад.
Досить слів, давайте коротко налаштуємо і перевіримо, як це працює. Як я згадував, нам потрібні два геми rbs
та steep
.
Отже, давайте встановимо їх за допомогою gem install rbs steep
Після того як ви це зробите, потрібно ініціалізувати Steep за допомогою команди steep init
. Ця команда створить файл Steepfile
— конфігураційний файл, який скаже Steep, як перевіряти ваш код. Нам наразі не потрібно багато налаштувань, але давайте відкриємо файл і замінимо вміст на:
target :app do
signature "sig"
check "func.rb"
end
Це вказує Steep перевіряти лише наш ruby-скрипт і брати підписи з папки sig
. Для цього прикладу нам не потрібно нічого іншого. Тепер додамо відсутні частини. Спочатку створимо нову папку — sig
, де ми зберігатимемо підписи для нашого коду.
Далі, я створю два файли: перший — це func.rb
:
def factorial(n)
return 1 if n <= 1
n * factorial(n - 1)
end
puts factorial(1)
puts factorial("1")
А потім відповідний підпис для нього у файлі sig/func.rbs
:
class Object
def factorial: (Integer n) -> Integer
end
Тепер, коли я запущу команду steep check
, я отримаю наступний вивід:
# Перевірка типів файлів:
.F
func.rb:8:15: [error] Не можна передати значення типу `::String` як аргумент типу `::Integer`
│ ::String <: ::Integer
│ ::Object <: ::Integer
│ ::BasicObject <: ::Integer
│
│ Діагностичний ID: Ruby::ArgumentTypeMismatch
│
└ puts factorial("1")
Це повідомляє нам, що ми передаємо значення невідповідного типу у функцію. Навіть не запускаючи жодного тесту, Steep дозволяє нам запобігти багатьом помилкам і зробити програмне забезпечення більш надійним.
З часом ви звикнете писати підписні файли першими, а реалізацію коду Ruby — пізніше. Це схоже на підхід у класичному TDD, коли спочатку пишеться тест, а потім відповідна логіка. **Ви все одно повинні працювати над покриттям тестами, але з RBS ви отримуєте додатковий шар безпеки.**
Підсумовуючи, впровадження статичної типізації в Ruby принесло численні переваги як розробникам, так і організаціям:
1. **Покращена якість коду:** Анотації типів слугують формою документації, що полегшує розуміння і підтримку коду.
2. **Раннє виявлення помилок:** Статична перевірка типів виявляє помилки на етапі компіляції, зменшуючи ймовірність помилок під час виконання.
3. **Покращена підтримка інструментів:** IDE та редактори тепер пропонують кращі функції, як-от інтелектуальний автозаповнення та підсвічування помилок, що працюють завдяки RBS.
4. **Легше співробітництво:** В командних середовищах анотації типів забезпечують спільне розуміння структур даних і інтерфейсів.
5. **Масштабованість:** Статична типізація додає передбачуваності великим проектам, полегшуючи їх масштабування та рефакторинг.
Є ще багато інших можливостей, які я не буду детально розглядати в цьому блозі. На даний момент RBS та інші інструменти надають повне покриття стандартної бібліотеки Ruby прямо "з коробки", а також підтримку для багатьох популярних гемів, включаючи Ruby on Rails. Існують способи автоматично генерувати підписи для вашого коду (хоча вони поки що не зовсім точні, але це хороший старт для роботи з застарілим програмним забезпеченням) і навіть писати підписи прямо в коді.
До цього часу я описував лише позитивні або легкі аспекти статичної типізації в Ruby. Чи дійсно все так просто і зрозуміло? Найпростіша відповідь — так, але, звісно, є деякі нюанси, які потрібно враховувати.
Окрім зміни мислення, про яку я вже згадував, досвід розробників ще не є настільки вдосконаленим. Наразі ви отримуєте два геми, які дозволяють писати підписи для вашого коду і перевіряти, чи все в порядку, виконуючи перевірку типів. Ви також отримаєте підтримку IDE (будь то RubyMine чи VS Code). Однак, оскільки всі ці частини активно розвиваються, можуть бути випадки, коли деякі функції тимчасово перестають працювати (до їх виправлення).
Якщо ви переглянете інші блоги на цю тему з попередніх років, ви побачите, як значно розвинулась вся екосистема навколо статичної перевірки типів.
Тепер це не просто набір експериментів, а досить стабільний набір інструментів, готових до використання в продуктивному середовищі, хоча з деякими маленькими ризиками, які слід враховувати.
Екосистема статичної типізації в Ruby розвивається дуже швидкими темпами. Чи це RBS, чи Sorbet, ви можете отримати всі необхідні функції та документацію прямо зараз. Очікування завершилося. Перехід може здатися складним, але тільки на перший погляд. Як тільки ви приймете зміни в мисленні, решта буде просто справою часу.
Особисто для мене це стало переломним моментом у проектах, над якими я працюю. Це дає мені таку впевненість у тому, як працює код. Я почав впроваджувати статичну типізацію, коли вона була лише нещодавно випущена в 2020 році, і мені доводилося шукати багато рішень самостійно або чекати, поки хтось інший знайде правильний шлях. **Ви ж зараз можете зекономити час** і **вивчити все, що потрібно для роботи з RBS, всього за кілька годин** за моїм [відео-курсом](https://www.udemy.com/course/ruby-on-types-write-robust-software-with-rbs/).
Зростає потреба підвищувати обізнаність про статичну типізацію в Ruby — потужний інструмент, який все ще чекає на прийняття розробниками Ruby по всьому світу. Ось чому я публікую щомісячну розсилку, що охоплює RBS та Sorbet. [Приєднуйтесь!](https://static-ruby.eremin.eu/)
Перекладено з: [The State of Static Typing in Ruby in 2025](https://aeremin.medium.com/the-state-of-static-typing-in-ruby-in-2025-6d64a53fd529)