はじめに
Ruby を触り始めた頃、チュートリアルや書籍では「綺麗なコード」「テストファースト」「gem は便利」といった理想が並んでいました。しかし、実際のプロジェクトに入ってみると、依存関係の地獄、テストが書かれていないレガシーなコード、デプロイ時にだけ発生するエンコーディング問題など、教科書には載っていない「現場特有の癖」に毎日直面します。私も最初の半年は「自分にはまだ早い」と感じ、夜中にスタックオーバーフローを漁る日々でした。この記事では、そんな現場で何度も転びながら身につけた、Ruby を「使いこなす」ための具体的な習慣や判断基準を、少し先を歩く先輩として共有します。読み終わったときに「明日から試せることが一つでも増えた」と思ってもらえれば嬉しいです。
✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨
スイカゲームとにゃんこ大戦争のようなタワーディフェンス系ゲームを組み合わせたゲームを作成しました!
遊んでみていただけると嬉しいです🙇♂️
ハジメル.dev: https://hajimeru-dev.vercel.app/
「ひとりで続けるのは難しい」「何から学べばいいか分からない」という方向けに、
プログラミングのマンツーマンレッスンサービス「ハジメル.dev」も運営しています。
未経験OK・オンライン完結・月額制/違約金なしなので、気軽に無料相談してみてください🙇♂️
海外テックニュースを追いたいけど、英語や情報量の多さで大変…という方向けに、
Hacker News の話題を日本語でサクッと追える「HackerNews 日本語まとめ & AI要約」
を個人開発しました!
技術トレンド収集に使ってもらえると嬉しいです🔥🙇♂️
→ HackerNews 日本語まとめ & AI要約: https://hn-matome-2ht.pages.dev/
「ニャンパイアサバイバー」というヴァンパイアサバイバーリスペクトのゲームを作成しました!
もしよろしければ遊んで頂けると嬉しいです😭
習い事教室の先生向けに、SNS 投稿・生徒募集・保護者通知の文章を AI で生成する Web サービス「おしらせAI」を個人開発しました。Next.js + Supabase + LLM で構成しており、無料で月 10 回まで試用できます。よければ触ってみてください。
→ おしらせAI: https://oshirase-ai.vercel.app/
✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨
gem の選び方と依存地獄を避ける
プロジェクト立ち上げの最初の週、先輩から「まず Gemfile を整理しろ」と言われたとき、私は「gem を増やせば機能が増える」と思い込み、気軽に gem 'devise', gem 'cancancan', gem 'sidekiq', gem 'redis' などを追加しました。結果、bundle install に 5 分以上かかり、CI で依存解決エラーが頻発。さらに、ある gem が別の gem とバージョン競合を起こし、本番で LoadError が出る事態に。この経験から学んだのは、「gem は最小限にし、公式ドキュメントの『依存関係』セクションを必ず確認する」という原則です。具体的には、以下のチェックリストを毎回実行しています。
- 目的が明確か – その機能を自前で書けるなら gem を入れない。
- メンテナンス状況 – 最後のリリースが 1 年以上前なら避ける。
-
依存ツリーの深さ –
bundle vizで可視化し、深すぎる場合は代替を探す。 - ライセンス – 商用利用で問題ないか確認。
# Gemfile 例:最小構成
source 'https://rubygems.org'
gem 'rails', '~> 7.1'
gem 'pg', '~> 1.5'
gem 'puma', '~> 6.4'
# 認証は自前で実装するため devise は入れない
gem 'sidekiq', '~> 7.2' # 非同期ジョブが必要な場合のみ
gem 'redis', '~> 5.1', require: false
また、bundle outdated を週次で実行し、セキュリティパッチが出たら即座にアップデートする運用にしています。これにより「依存地獄」にハマる頻度が劇的に減り、新人メンバーが加わったときも bundle install が 30 秒以内で終わる環境を維持できています。
テストを書く習慣が現場を救う
「テストは書くもの」と頭では分かっていても、納期優先で後回しにしがちです。私が痛感したのは、あるリリース直前に User#full_name が nil を返すバグを見逃し、本番でユーザー名が空白で表示された事件です。当時、モデルに対する単体テストがゼロだったため、リグレッションに気づく手段がありませんでした。それ以来、以下のルールを自分に課しています。
-
新規機能には必ず RSpec の
describeブロックを一つ作る – 即使是最小のit 'returns full name'でも良い。 - 既存コードを触る前には、そのメソッドをカバーするテストを先に書く – 「テスト駆動でリファクタ」のミニ版。
-
CI で
bundle exec rspec --fail-fastを必須化 – 失敗したら即マージブロック。
# spec/models/user_spec.rb
require 'rails_helper'
RSpec.describe User, type: :model do
describe '#full_name' do
it 'returns concatenated first and last name' do
user = build(:user, first_name: '太郎', last_name: '山田')
expect(user.full_name).to eq '山田 太郎'
end
it 'handles missing first_name gracefully' do
user = build(:user, first_name: nil, last_name: '山田')
expect(user.full_name).to eq '山田'
end
end
end
この習慣のおかげで、リファクタリング時に「テストが通れば安心」という感覚が身につき、デグレードゼロのリリースを半年続けられています。テストコード自体もドキュメント代わりになり、新メンバーが仕様を把握するスピードが上がりました。
リファクタリングのタイミングを見極める
「綺麗なコードにしたい」という気持ちは大切ですが、現場では「今すぐ動くコード」が優先される場面が多々あります。私が失敗したのは、機能追加の合間に大規模なリファクタを入れようとし、ブランチが 2 週間もマージされず、他のメンバーがコンフリクトで苦労したことです。そこから学んだのは、「リファクタリングは『小さく、頻繁に、テストが緑の状態で』行う」という原則です。具体的な運用として以下を徹底しています。
- ボーイスカウトルール – ファイルを開いたら、その場で 1〜2 行の改善(命名の修正、不要なコメント削除、マジックナンバーの定数化)だけ行う。
- 専用ブランチで 1 時間以内に完了 – 大きな構造変更は Issue 化し、スプリントの最初に計画する。
-
リファクタ前後に
rubocop -aとbundle exec rspecを実行 – スタイル統一と回帰テストを自動化。
# リファクタ前
def calculate_total(items)
total = 0
items.each { |i| total += i.price * i.quantity }
total
end
# リファクタ後(ボーイスカウトルール適用)
def calculate_total(items)
items.sum { |item| item.price * item.quantity }
end
このように小さな改善を積み重ねると、半年後には「あの頃のスパゲッティコードがどこにあったか思い出せない」ほどクリーンなコードベースになります。また、レビュー時に「ここだけ直した」という小さな PR は承認が速く、チーム全体の心理的負担も減ります。
コードレビューで磨く「読みやすさ」
コードレビューは単なるバグ探しではなく、チーム全体の「読みやすさ」の基準を共有する場です。最初の頃、私は「動けば OK」と思い、変数名を x, tmp, data など適当につけていました。先輩から「この data が何を指しているか、1 ヶ月後に自分で読めるか?」と問われ、ハッとしました。それ以来、レビュー時に以下のポイントを意識してコメントし、自分も同じ基準で書くようにしています。
-
意図が伝わる命名 –
user_recordsではなくactive_users、calcではなくcalculate_monthly_fee。 - メソッドの単一責任 – 1 メソッド 10 行以内、引数 3 つ以内を目安に分割。
-
早期リターンでネストを浅く –
return unless conditionを活用し、if/elseの深さを 2 段以内に。
# レビュー前(ネスト深い)
def charge(user, plan)
if user.active?
if plan.paid?
if user.credit_card.present?
PaymentGateway.charge(user.credit_card, plan.amount)
else
raise 'クレジットカードが登録されていません'
end
end
end
end
# レビュー後(早期リターン+単一責任)
def charge(user, plan)
return unless user.active?
return unless plan.paid?
raise 'クレジットカードが登録されていません' unless user.credit_card.present?
PaymentGateway.charge(user.credit_card, plan.amount)
end
レビューでこの指摘を受けたとき、最初は「面倒だ」と思いましたが、実際にこのスタイルで書き直すと、後から自分が読み返したときに「何をしているコードか即座に理解できる」ことに気づきました。チーム内でこの基準が共有されると、新人でも 1 週間で「読みやすいコード」を書けるようになり、レビューの往復が減り、デプロイ頻度が上がりました。
デプロイ・運用で気をつけたい Ruby の癖
最後に、本番環境特有の Ruby の挙動でハマった経験を共有します。ある夜、デプロイ後に Encoding::CompatibilityError が大量に発生し、ログが文字化けして原因特定に 3 時間かかりました。原因は、開発環境が UTF-8 で統一されていたのに対し、本番の DB 接続設定で encoding: utf8mb4 が抜けていたこと、さらに config.encoding = 'utf-8' が application.rb になかったことです。これを防ぐために、以下のチェックリストをデプロイ前の「リリース前チェック」として CI に組み込みました。
-
config.encoding = 'utf-8'がconfig/application.rbにあるか。 -
database.ymlのencoding: utf8mb4とcollation: utf8mb4_general_ciが全環境で設定されているか。 -
Bundle exec rails assets:precompileがエラーなく通るか(SassC のエンコーディング問題を早期検知)。 -
bundle exec sidekiq -C config/sidekiq.ymlでジョブキューが正常に起動するか。
# config/database.yml(本番例)
production:
<<: *default
database: myapp_production
username: <%= ENV['DB_USER'] %>
password: <%= ENV['DB_PASSWORD'] %>
encoding: utf8mb4
collation: utf8mb4_general_ci
また、Ruby の GC チューニング(RUBY_GC_HEAP_GROWTH_FACTOR など)も本番メモリ使用量を安定させるために設定しています。これらを自動化しておくことで、「深夜のデプロイでまたエンコーディングか」と慌てることがなくなり、安心してリリースできるようになりました。
まとめ
現場で Ruby を書き続ける中で、gem の選定、テスト習慣、リファクタリングの粒度、コードレビューの基準、デプロイ前のエンコーディングチェック――これら五つのポイントを意識するだけで、日々の開発ストレスが大きく減り、チーム全体の生産性が上がりました。完璧を目指す必要はなく、まずは一つずつ自分のワークフローに取り入れてみてください。明日からでも「ボーイスカウトルールで 1 行直す」「新機能に 1 テスト書く」といった小さなアクションから始めれば、半年後には確実に「現場で使える Ruby エンジニア」に近づいています。一緒にコツコツ積み上げていきましょう。