RubyMiddleExperience
Какие ошибки делают разработчики, переходящие на Ruby с другого языка или стека?
Переходящие на Ruby чаще всего переносят паттерны Java/PHP (процедурный стиль, ранние return-guards, статическую типизацию), недооценивают GVL и пишут Rails-код без понимания, когда выполняются callbacks.
1. Перенос стиля из Java или PHP
Разработчик с Java-бэкграундом пишет:
# Java-style Ruby (плохо):
class UserService
def self.create_user(name, email)
user = User.new
user.name = name
user.email = email
if user.valid?
user.save
return user
else
return nil
end
end
end
# Идиоматичный Ruby:
class UserService
def self.create_user(attrs)
User.create(attrs) # ActiveRecord уже делает valid? + save
end
end
# Или с явной обработкой ошибок:
User.create!(name: name, email: email) # бросает если invalid
2. Непонимание, когда выполняются Rails-callbacks
Часто переходящие думают, что before_save выполняется при любом обращении к объекту:
class Order < ApplicationRecord
before_save :calculate_total
private
def calculate_total
self.total = line_items.sum(&:price)
end
end
# Проблема: update_column обходит callbacks!
order.update_column(:status, 'shipped') # calculate_total НЕ вызовется
order.update(status: 'shipped') # calculate_total вызовется
3. Игнорирование GVL при работе с потоками
# Ошибка: ожидание параллельного CPU-bound выполнения
results = data_chunks.map do |chunk|
Thread.new { expensive_cpu_computation(chunk) }
end.map(&:value)
# Потоки не ускоряют CPU-bound задачи в MRI!
# Правильно для CPU-bound:
results = data_chunks.map do |chunk|
fork { expensive_cpu_computation(chunk) }
end
# или используйте Ractor (Ruby 3+)
4. Monkey-patching стандартных классов в неправильном месте
# Опасно: добавление метода к String глобально
class String
def to_slug
downcase.gsub(/\s+/, '-')
end
end
# Лучше: refinements — ограниченный scope
module SlugRefinement
refine String do
def to_slug
downcase.gsub(/\s+/, '-')
end
end
end
class Article
using SlugRefinement # активно только в этом классе
end
5. Использование символов и строк непоследовательно
# Часто от PHP/Python — перемешивание:
params = { 'name' => 'Alice', 'role' => 'admin' }
# В Rails params — HashWithIndifferentAccess, в чистом Ruby — нет:
params[:name] # => nil (если обычный Hash со строками-ключами)
params['name'] # => 'Alice'
# Договоритесь о стандарте:
config = { host: 'localhost', port: 5432 } # символы для внутренних Hash
6. Игнорирование идиом Enumerable
# Императивно (из PHP/Java):
result = []
users.each do |u|
result << u.name if u.active?
end
# Идиоматично:
result = users.select(&:active?).map(&:name)
Подводные камни
- rescue Exception вместо rescue StandardError — перехватывает SystemExit и Interrupt. Типичная ошибка из PHP/Python-привычки писать catch(Exception).
- Неиспользование Bundler — разработчики из Python иногда ставят gems глобально. Всегда работайте через Gemfile + bundle exec.
- attr_accessor для всего — Java-разработчики открывают все поля через accessor. В Ruby инкапсуляция через attr_reader + приватные setters.
- Забытый frozen_string_literal — в production Rails-приложениях добавьте magic comment в Gemfile или через initializer для снижения аллокаций строк.
- Отсутствие тестов на поведение callbacks — callbacks тестируйте интеграционно, а не только unit; иначе пропускаете edge cases с update_column, touch, import.
What hurts your answer
- Перечислять ошибки без объяснения причин
- Не отличать beginner mistakes от production failure modes
- Не предлагать процесс, который предотвращает повторение ошибок
What they're listening for
- Знает типичные ошибки при работе с Ruby
- Понимает причины ошибок
- Предлагает практики prevention и early detection