ActiveRecord High severity

Fat Models

An ActiveRecord model that accumulates unrelated business logic, third-party integrations, validations, and orchestration over time, typically growing past 500 lines and encapsulating responsibilities that span multiple bounded contexts.

Before / After

Problematic Pattern
class Order < ApplicationRecord
# 1,200 lines
def process_payment! ... end
def calculate_tax ... end
def calculate_shipping ... end
def generate_invoice_pdf ... end
def send_confirmation ... end
def sync_to_erp ... end
def refund! ... end
def apply_loyalty_points ... end
# plus 30 validations and 12 callbacks
end
Target Architecture
class Order < ApplicationRecord
# State, associations, validations only
end

class Orders::Checkout
include Dry::Transaction
step :validate_cart
step :process_payment
step :calculate_tax
step :generate_invoice
step :send_confirmation
end

# Call site
Orders::Checkout.new.call(cart_params)

Why this hurts

God objects concentrate risk. Every feature touches the model file, every merge conflicts on it, every memory leak or slow test traces back to a class that belongs to no one and everyone at once. Ruby loads the entire class into memory on every autoload cycle, so a 1,200-line file with dozens of constants inflates boot time in development and adds object allocation overhead in production. The in-memory representation of one Order instance pulls associations, validators, and registered callbacks into the object graph, tripling the garbage collector’s workload per request.

Testing becomes slower than the production path. Factories must set up unrelated collaborators because validations cross-reference them. A single Order spec file reaches thousands of lines and takes minutes to run, because instantiating the model exercises payment logic, tax logic, shipping logic, and invoice logic simultaneously. Coverage reports flatter than reality: testing one arithmetic branch hits fifty unrelated lines and inflates the coverage percentage while leaving real behaviors untested.

Concurrency suffers too. Class-level constants, @@ variables, and memoized class methods accumulate in god objects as a convenience, introducing shared mutable state that misbehaves under Puma’s threaded mode. Rails’ code reloader cannot safely reload the class mid-request because too many collaborators depend on its internal state, so changes require a full server restart in development. In production, a bug in any one method can corrupt the object’s in-memory state and silently affect every subsequent request on the same worker thread until the process recycles.

See also: Refactoring Fat Models into Rails Service Objects.

Get Expert Help

Inheriting a legacy Rails codebase with this problem? Request a Technical Debt Audit.