Mathias Meyer
Mathias Meyer

Tags

Recently I’ve been having a foul taste in my mouth, or just a bad feeling, if you will. Whenever I started adding validations and callbacks to a model in a Rails application. It just felt wrong. It felt like I’m adding code that shouldn’t be there, that makes everything a lot more complicated, and turns explicit into implicit code. Code that is only being run depending on the persistence state of an object. Code that is being hard to test, because you need to save an object to test parts of your business logic. Don’t get me started on observers, I never was a fan of them. Putting stuff that should be run when an object was saved elsewhere is the worst kind of hiding business logic.

The two are somewhat bound together: Create an object, run a callback and validations. Update an object, check if an attribute is set, run a different callback. I started to loathe the lack of implicitness that callbacks gave my code, and I started to loathe that libraries everywhere copied that behaviour from ActiveRecord, and that you involve testing your database in your unit tests, though implicitly, and that noone had a better answer for how that stuff should work. Sure, it’s not big news that involving your database in your unit tests is not a recommended practice, hence the overuse of mocking and stubbing fostered by RSpec, but it took a while for me to really feel the pain.

Ingredients: REST, Rails, Resources

The root problem (in a very philosophical way and in my very own opinion) lies in Rails’ understanding of RESTful resources. The path to a resource is supposed to be thin, and a resource is mostly represented by something that’s stored in a database. So far, so good. That thin layer however, the controller, implies that running any logic on a resource somehow ends up in simply updating the resource, setting some attribute signaling that an action needs to be taken. That’s how tools like resource_controller, make_resourceful and inherited_resources came about, to get rid of the thin-layered controller tier, the boiler plate code. And I loved them, because I hated writing the same code over and over again.

But over time I realized that controllers aren’t the problem, it’s the way Rails treats resources that lead us to this mess where everything’s declared in a model’s class body, where you have to look at callbacks to find out what’s going on when you create a new object, and where you clutter your models with code that just doesn’t need to be there (sending mails, for example, or doing custom validations that are bound to specific state). It all started to lack explicitness for me. There’s no clear path of entry, no list of method calls you can follow along.

I’m not sure how other people see this, obviously a lot of people don’t realize it’s a problem, or they’re simply fine with how things work, I was for a long time too, almost five years now, to be exact. I know I’m not alone in this though, talking to others made me realize they’re on a similar page, but given that it’s the way Rails works, what can you do?

Validations

What about validations? I can live with validations being part of the model, but they’re still part of the problem generated by callbacks, simply because validations are callbacks themselves. I always found myself ending up writing custom validations in before_validation et. al. hooks, and it just felt wrong, because the list of callbacks grew noticably. Validations are not always the same depending on your object’s state. They can even be quite useless when you’re handling a lot of backend code that’s asynchronous and decoupled from the web interface.

What do we do to work around it? We resort to using save(false), whose lack of explicitness I cannot even begin to fully explain in a peaceful manner. Validations tend to depend on the object’s state, at least more complex ones. They’re not universal, they can be different when creating and saving objects. Yes, I know that in ActiveRecord you can say :on => :create, but for me that just emphasizes my point. Persistent state determines callbacks and therefore business logic, decreasing expressiveness.

Forms? Presenters?

I’ve looked into how Django is dealing with this, and it sort of struck a chord with me. Their approach is to define form objects that do the validations for you. They’re somewhat decoupled from your business logic and are your intermediate layer between the web interface and the business model. Good lord, did I just recommend we all use presenter objects, even though Jay Fields said they don’t work? I think I did, Bob!

It’s not the way, but it’s one way, and there’s been work on bringing this to Rails, have a look at Bureaucrat for example. Again, it’s one way to try solving the problem, it doesn’t come without any downsides too. Tying the idea of explicit services to form data may or may not make unit testing harder, or at least mix two different concerns (forms and creating/manipulating objects). What I like about it though is that it decouples validation from the business logic, and ties it to the layer it’s really necessary, the view.

Not without irony, I’ve started using something similar to handle embedded objects on a project using CouchDB. I wanted to extract out the stuff handling with embedded objects, because I was simply to lazy to add support for that to CouchPotato. The result felt amazingly good, almost like the above forms mixed with a *gasp* service.

Services?

I’m still seeking for the answer to that one. Pat Maddox and James Golick suggest services as one way to make callbacks explicit, be sure to read through the comments on James’ post. Now, I don’t know about your history, but coming from a Java background that struck a bad cord with me. I laughed off the idea, to be frank.

One thing still bugs me about the approach James is laying out in his post, it’s the extensive use of mocking. It sure does make for fast unit tests, and I’m starting to see the sense in it when using a service model, but I’ve grown fond of the fakes approach, which is why we wrote Rocking Chair, and why people came up with things like SuperModel, in-memory database implementations you can easily switch on and off, not exactly decoupling your unit tests from the persistence layer, but decoupling them from having a database running, making it easier to e.g. split batches of tests across several cores and processors, and simply making them faster.

However, the more I thought about it, the more sense the services approach made. You don’t have to necessarily make it a separate class, depending on how much you like having all of your business logic in the model. I’m not sure which approach I’d prefer, because honestly, I haven’t tried it yet, but I’m willing to give in. I’m willing to try things that let me ditch callbacks and validations, because I simply loathe them, they just don’t feel right to me anymore when used in the way ActiveRecord does.

With Rails 3 for example, you can put your validations anywhere, they’re not just bound to ActiveRecord anymore, they leave a lot more room for you to use them in different ways. I’m not suggesting you should get rid of validations, they’re just a part of the callbacks madness, and they tend to not always be the same depending on what you’re doing in a particular use case. Using save(false) just started to feel like a bad workaround to me to avoid having callbacks run. I want to save an object no matter what, and run whatever code I require in that course of action, in an explicit fashion.

Are Fine-grained Resources the Future?

The question that’s left for me, and this is where I’m starting to gasp question Rails’ approach to resources. I’m starting to think they’re wrong, and that for the service or the forms model to work, the idea of a single resource you update whenever you want your model to do something needs to change. Having very specific services and their methods would mean a switch to more lightweight and finer-grained resources, specific resources where you e.g. accept an invitation not by PUTting to /invitation/12345, but instead to /invitation/12345/accept.

But, and here’s the kicker, to me this leaves the question of how RESTful that approach is. This is where I’d like to get input. How do people using services already solve this problem? Have lots of tiny methods in your controllers? Maybe it’s time to work more with Sinatra to get different ideas in my head, to rethink resources. I’m still not certain what a good and RESTful way to approach this would be. Whatever it is, it’d leave my business logic in a way that’s a lot more approachable both for people reading it, and for my tests. That’s something pretty dear to me, and more important than fully conforming to a REST model. There must be something better, because the current status quo of handling business logic with Rails’ resources just doesn’t work for me anymore, it doesn’t make me a happy programmer.

The answers have yet to be found. My intention is not to rant, but rather to start a discussion around it. In my opinion the way Rails recommends handling resources doesn’t have a future, if only for me, and even if that means using other frameworks in the future. I don’t think we wasted time with Rails’ current way, instead we’re going to see an evolution, and I find that pretty exciting, because the future has yet to be written in this regard, and we can be a part of that.