dhh-ruby-style

DHH Ruby/Rails Style Guide

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "dhh-ruby-style" with this command: npx skills add microck/ordinary-claude-skills/microck-ordinary-claude-skills-dhh-ruby-style

DHH Ruby/Rails Style Guide

Write Ruby and Rails code following DHH's philosophy: clarity over cleverness, convention over configuration, developer happiness above all.

Quick Reference

Controller Actions

  • Only 7 REST actions: index , show , new , create , edit , update , destroy

  • New behavior? Create a new controller, not a custom action

  • Action length: 1-5 lines maximum

  • Empty actions are fine: Let Rails convention handle rendering

class MessagesController < ApplicationController before_action :set_message, only: %i[ show edit update destroy ]

def index @messages = @room.messages.with_creator.last_page fresh_when @messages end

def show end

def create @message = @room.messages.create_with_attachment!(message_params) @message.broadcast_create end

private def set_message @message = @room.messages.find(params[:id]) end

def message_params
  params.require(:message).permit(:body, :attachment)
end

end

Private Method Indentation

Indent private methods one level under private keyword:

private def set_message @message = Message.find(params[:id]) end

def message_params
  params.require(:message).permit(:body)
end

Model Design (Fat Models)

Models own business logic, authorization, and broadcasting:

class Message < ApplicationRecord belongs_to :room belongs_to :creator, class_name: "User" has_many :mentions

scope :with_creator, -> { includes(:creator) } scope :page_before, ->(cursor) { where("id < ?", cursor.id).order(id: :desc).limit(50) }

def broadcast_create broadcast_append_to room, :messages, target: "messages" end

def mentionees mentions.includes(:user).map(&:user) end end

class User < ApplicationRecord def can_administer?(message) message.creator == self || admin? end end

Current Attributes

Use Current for request context, never pass current_user everywhere:

class Current < ActiveSupport::CurrentAttributes attribute :user, :session end

Usage anywhere in app

Current.user.can_administer?(@message)

Ruby Syntax Preferences

Symbol arrays with spaces inside brackets

before_action :set_message, only: %i[ show edit update destroy ]

Modern hash syntax exclusively

params.require(:message).permit(:body, :attachment)

Single-line blocks with braces

users.each { |user| user.notify }

Ternaries for simple conditionals

@room.direct? ? @room.users : @message.mentionees

Bang methods for fail-fast

@message = Message.create!(params) @message.update!(message_params)

Predicate methods with question marks

@room.direct? user.can_administer?(@message) @messages.any?

Expression-less case for cleaner conditionals

case when params[:before].present? @room.messages.page_before(params[:before]) when params[:after].present? @room.messages.page_after(params[:after]) else @room.messages.last_page end

Naming Conventions

Element Convention Example

Setter methods set_ prefix set_message , set_room

Parameter methods {model}_params

message_params

Association names Semantic, not generic creator not user

Scopes Chainable, descriptive with_creator , page_before

Predicates End with ?

direct? , can_administer?

Hotwire/Turbo Patterns

Broadcasting is model responsibility:

In model

def broadcast_create broadcast_append_to room, :messages, target: "messages" end

In controller

@message.broadcast_replace_to @room, :messages, target: [ @message, :presentation ], partial: "messages/presentation", attributes: { maintain_scroll: true }

Error Handling

Rescue specific exceptions, fail fast with bang methods:

def create @message = @room.messages.create_with_attachment!(message_params) @message.broadcast_create rescue ActiveRecord::RecordNotFound render action: :room_not_found end

Architecture Preferences

Traditional DHH Way

PostgreSQL SQLite (for single-tenant)

Redis + Sidekiq Solid Queue

Redis cache Solid Cache

Kubernetes Single Docker container

Service objects Fat models

Policy objects (Pundit) Authorization on User model

FactoryBot Fixtures

Detailed References

For comprehensive patterns and examples, see:

  • references/patterns.md

  • Complete code patterns with explanations

  • references/resources.md

  • Links to source material and further reading

Philosophy Summary

  • REST purity: 7 actions only; new controllers for variations

  • Fat models: Authorization, broadcasting, business logic in models

  • Thin controllers: 1-5 line actions; extract complexity

  • Convention over configuration: Empty methods, implicit rendering

  • Minimal abstractions: No service objects for simple cases

  • Current attributes: Thread-local request context everywhere

  • Hotwire-first: Model-level broadcasting, Turbo Streams, Stimulus

  • Readable code: Semantic naming, small methods, no comments needed

  • Pragmatic testing: System tests over unit tests, real integrations

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

alex-hormozi-pitch

No summary provided by upstream source.

Repository SourceNeeds Review
General

dnd5e-srd

No summary provided by upstream source.

Repository SourceNeeds Review
General

shopify-api

No summary provided by upstream source.

Repository SourceNeeds Review
General

analyzing-financial-statements

No summary provided by upstream source.

Repository SourceNeeds Review