service-objects-vs-fat-models

Installation
SKILL.md

Service Objects vs Fat Models

Rails has one of the longest-running community debates over where business logic should live. AI agents reach for service objects on every controller action — over-extracting. Senior Rails devs keep logic on the model until it earns the trip out. This skill encodes the "earn it" criteria.

The opinion (DHH-leaning, with explicit exceptions)

Default: keep logic in the model. Split fat models with ActiveSupport::Concern. Extract a service object only when (a) the workflow orchestrates 3+ models in one transactional unit, (b) the workflow calls an external API, (c) the workflow has multiple distinct outcomes that callers must branch on, or (d) the workflow has no obvious model home.

Counter-position: at very large team size (20+ engineers) or in domains with heavy procedural business logic (insurance underwriting, multi-step billing, regulated workflows), small-object-per-responsibility scales reading better than fat models. We acknowledge that and recommend it — but only as a deliberate choice, not as the default. The Sandi Metz / "single responsibility" doctrine is real but expensive when applied prematurely.

DHH on this:

"I far prefer current_account.posts.visible_to(current_user) to involving a third query object."

"We have used this approach for about 8 years in the Basecamp code base and been so happy with the result that every subsequent app we've made has followed the same pattern with great results."

Core patterns

Pattern 1: The default — fat model

Installs
1
GitHub Stars
21
First Seen
Sep 8, 2026
service-objects-vs-fat-models — sandeepmvl/rails-skills