I read Bryan Helmkamp's blog post 7 Patterns to Refactor Fat ActiveRecord Models when it was originally posted, but it's taken me until recently to implement some of his suggestions in TalentSoup. Specifically, I've been writing service objects, using similar criteria to Bryan.
One quick example of how I've used service objects is in the downgrading process. Previously, my code for downgrading an account looked like this:
user.rb class was responsible for post-downgrade cleanup in the
downgrade_brand_to method, like canceling the subscription with Chargify, and resetting some of the features availabile to our Pro users. The code certainly worked, but there were a number of things wrong the approach (besides how obviously hideous it is!):
Spread out the business logic behind canceling across a controller and a model. I always want to log the reason for a cancellation along with doing the actual canceling, but the previous implementation had those two occuring separately.
Since there was no single point of entry that would encapsulate the entire downgrade process, it was hard to test.
It was also not portable. We might want to downgrade in other parts of the app too (reconciliation process via Rake task, perhaps) and the current process would necessitate repeating ourselves, and possibly leaving out something important.
My refactored code now looks like this:
I think this is much cleaner, it's reusable, and it's easier to test.
To be quite honest, as great as those things are, the biggest win for me so far has been that I feel better about the application. I feel more confident going in to change things, because I can focus on much smaller parts of the application, rather than digging into God models. As a solo developer, it becomes harder to keep the whole application in your head as the code base grows. Therefore, anything that will express business logic in a concise way and make it easier to come back and understand months later ("I just need to look at one small class to see what we do when we downgrade") is huge.