Stage 1: クリーンコード — 読みやすいコードの基礎
「動く」と「良い」は違う
翌朝、ヒロシはマイに声をかけた。
「昨日のロードマップ、家で考えてたんですけど……クリーンコードって結局、きれいに書くってことですよね?インデントとか?」
マイは首を横に振った。
「きれいに見える、じゃなくて。読んだ人が一瞬で意図を理解できること。コードは書いた瞬間より、読まれる時間の方がずっと長い。」
命名はコードの語彙
「まず命名から始めよう。」
マイはヒロシのコードを開いた。
# 悪い例
def calc(u, p, d)
r = u.pts * 0.1
if p > 10000
r = r + (p - 10000) * 0.05
end
if d == "express"
r = r + 500
end
r
end「u, p, d, r。何これ?」
「ユーザー、価格、配送方法、割引額……だと思います。」
「思う、でしょ?確認しないとわからない。コードを読むたびに解読が必要になる。これが積み重なると、開発速度が何倍も遅くなる。」
# 良い例
def calculate_discount(user, purchase_price, delivery_type)
base_discount = user.points * 0.1
volume_discount = if purchase_price > 10_000
(purchase_price - 10_000) * 0.05
else
0
end
express_surcharge = delivery_type == "express" ? 500 : 0
base_discount + volume_discount - express_surcharge
end「変数名が増えたけど、意図が明確になった。base_discount, volume_discount, express_surcharge。それぞれが何を表すか、一目でわかる。」
INFO
良い命名の3原則
- 省略しない(
uよりuser、calcよりcalculate_discount) - 「何をするか」ではなく「何であるか」を表す(
dataよりactive_users) - 文脈に合わせた具体性(
get_infoよりfetch_user_profile)
Railsでの命名規約
Railsには慣習として確立した命名ルールがある。これに従うことが、クリーンコードへの最短経路だ。
# モデル名: 単数形・PascalCase
class UserProfile < ApplicationRecord
# スコープ: 動詞ではなく形容詞/状態を表す
scope :active, -> { where(status: "active") }
scope :premium, -> { where(tier: "premium") }
# メソッド: 動詞で始まる
def full_name
"#{first_name} #{last_name}"
end
# 真偽を返すメソッド: ?で終わる
def eligible_for_discount?
premium? && account_age_days > 30
end
# 副作用のあるメソッド: !で終わる
def upgrade_to_premium!
update!(tier: "premium", upgraded_at: Time.current)
end
end関数設計: 一つのことだけやる
次はヒロシがよく書くパターンを見た。
# 悪い例: 一つのメソッドが何でもやる
def process_order(order_id)
order = Order.find(order_id)
# 在庫チェック
order.items.each do |item|
if item.product.stock < item.quantity
raise "在庫不足: #{item.product.name}"
end
item.product.decrement!(:stock, item.quantity)
end
# 支払い処理
charge = Stripe::Charge.create(
amount: order.total_price,
currency: "jpy",
customer: order.user.stripe_customer_id
)
order.update!(payment_id: charge.id, status: "paid")
# 通知
OrderMailer.confirmation(order).deliver_later
SlackNotifier.notify("#orders", "注文確定: #{order.id}")
# ポイント付与
order.user.points.create!(amount: order.total_price / 100, reason: "purchase")
order
end「このメソッドが抱える問題は?」
ヒロシは考えた。「……在庫が減った後で支払いが失敗したら、在庫だけ減ってしまいます。」
「その通り。そして支払い処理をテストしたいだけなのに、Slackにも通知が飛ぶ。一つのメソッドに複数の責務が混在してる。」
# 良い例: 各メソッドが一つの責務を持つ
class OrderProcessor
def initialize(order)
@order = order
end
def call
ActiveRecord::Base.transaction do
reserve_inventory!
process_payment!
end
send_notifications
grant_loyalty_points
@order
end
private
def reserve_inventory!
@order.items.each do |item|
raise InsufficientStockError, item.product.name if item.product.stock < item.quantity
item.product.decrement!(:stock, item.quantity)
end
end
def process_payment!
charge = Stripe::Charge.create(
amount: @order.total_price,
currency: "jpy",
customer: @order.user.stripe_customer_id
)
@order.update!(payment_id: charge.id, status: "paid")
end
def send_notifications
OrderMailer.confirmation(@order).deliver_later
SlackNotifier.notify("#orders", "注文確定: #{@order.id}")
end
def grant_loyalty_points
@order.user.points.create!(
amount: @order.total_price / 100,
reason: "purchase"
)
end
end「OrderProcessor#callは全体の流れを把握するためだけにある。各ステップは独立したメソッドになって、テストも修正もしやすい。」
コメントよりもコード
「コメントをたくさん書けばわかりやすいと思ってたんですけど、違うんですか?」とヒロシ。
「コメントは嘘をつく。コードは嘘をつかない。」
# 悪い例: コメントが必要なコード
# ユーザーが30日以上前に登録していて、かつプレミアム会員で、
# かつ今月の購入金額が5000円以上の場合に割引を適用する
def apply_discount?(user)
user.created_at < 30.days.ago && user.premium? && user.monthly_purchases > 5000
end
# 良い例: コードが自己説明する
DISCOUNT_ELIGIBILITY_DAYS = 30
DISCOUNT_ELIGIBILITY_PURCHASE_AMOUNT = 5_000
def discount_eligible?(user)
established_account?(user) &&
user.premium? &&
sufficient_monthly_purchases?(user)
end
def established_account?(user)
user.created_at < DISCOUNT_ELIGIBILITY_DAYS.days.ago
end
def sufficient_monthly_purchases?(user)
user.monthly_purchases > DISCOUNT_ELIGIBILITY_PURCHASE_AMOUNT
endWARNING
コメントが有効な場面
コメントが本当に必要なのは「なぜこうしたか(Why)」を説明するときだけ。「何をしているか(What)」はコード自体が語るべきです。
# Stripe は金額をセント単位で受け取るため100倍する
amount_in_cents = price * 100Railsらしいクリーンコード
Railsには「Rail way」と呼ばれる慣用的なパターンがある。これを活用することがクリーンコードへの近道だ。
# scope を活用する
class Article < ApplicationRecord
scope :published, -> { where(status: "published") }
scope :recent, -> { order(published_at: :desc) }
scope :popular, -> { where("view_count > ?", 1000) }
# scopeを組み合わせて表現力豊かなクエリに
# Article.published.recent.popular
end
# Concern でロジックを整理する
module Auditable
extend ActiveSupport::Concern
included do
before_create :set_created_by
before_update :set_updated_by
end
private
def set_created_by
self.created_by = Current.user&.id
end
def set_updated_by
self.updated_by = Current.user&.id
end
end
class Order < ApplicationRecord
include Auditable
# 監査ロジックを再利用できる
endAWSでのコード品質管理
「コードの品質は、チームとツールで守る。」マイが続けた。
# .github/workflows/code_quality.yml
name: Code Quality
on: [pull_request]
jobs:
rubocop:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Run RuboCop
run: bundle exec rubocop --format github
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Brakeman Security Scan
run: bundle exec brakeman --no-pagerAWS CodeBuild でも同様のチェックを本番デプロイ前に実行できる。
# buildspec.yml (AWS CodeBuild)
version: 0.2
phases:
pre_build:
commands:
- bundle install
build:
commands:
- bundle exec rubocop
- bundle exec rspec
- bundle exec brakemanStage 1 のまとめ
ヒロシは一日でたくさんのことを学んだ気がした。
「クリーンコードって、センスじゃなくてルールだったんですね。」
マイは微笑んだ。「そう。そしてルールには理由がある。なぜそうするかを理解すれば、ルールがない場面でも正しい判断ができる。」
| 原則 | 内容 |
|---|---|
| 意図を表す命名 | 省略せず、文脈に合った名前をつける |
| 単一責務 | 一つのメソッドは一つのことだけする |
| 自己説明するコード | コメントよりもわかりやすいコードを書く |
| Railsの規約を活かす | scope, concern, naming convention を活用 |
「次はパラダイム。なぜオブジェクト指向が生まれたのか、その根本から理解しよう。」
クリーンコードの土台ができた。次のStageでは、そのコードを支える「思想」を学ぶ。