Hacker Newsnew | comments | show | ask | jobs | submit login

If I can overly generalize, and as a person who generally loves Ruby and Rails, I'll suggest that there are two schools of Rails app development emerging.

The first is what I'll term the "Classic School" or "DHH School" characterized by full-blown utilization of all Rails magic such as AR/AC callbacks, pretty skinny but not obsessively skinny controllers, an eschewance of service objects and more.

The "Emerging School" relies less on some aspects of Rails magic, and introduces patterns like services/interactors/DCI and seems to be more focused on building a domain models which attempt to be less coupled to the framework.

I think the Grouper folks are in the second camp given their previous blog post about interactors which showed up here on HN. Also, I think the desire to encapsulate authorization into a reusable object seems slightly more in the spirit of the emerging school.

What I find odd is that they'd offer a solution for authorization which is so bound up in Rails magic. It seems to me authorization is really part of the domain model and as such you would expect to find that code in whatever gets called by the controller (which I would expect to be something like an interactor, but in this blog post is just plain old Rails code.)

I think there's a lot of miscommunication going on - people are doing the same things but using different vocabulary to describe it - and so I'm not sure I agree with your characterization.

In a nutshell, almost all DCI/service object blog posts I have read seem to have almost all of their issues addressed by a liberal use of `concerns`.

In the end, I think we're all talking about how to separate "concerns/responsibilities/dependencies" into atomic units so our code is easier to reason about; it turns out my user object does a Ton Of Things and it becomes more maintainable once we split it up.

The schism in my mind comes more from random blog post declaring "We have seen the truth! and it is $new_jargon_here" and then DHH pops out of the wilderness and crankily declares "yes, yes, this is why we introduced xyz, jeez."

What strikes me most in the end is that most of the random blog posts seem to lack a coherent theory for why their code has improved, rendering most of the conversation around it moot. DHH on the other hand has strong, and well formed opinion on the subject which is why he can occasionally come through and refactor it along his mental model.

This is all to say: I have rather strong, if currently unarticulated, opinions on how to think about code organization, and I tend to be more on DHH's side when it comes to these debates. Furthermore, I'm increasingly convinced we're all talking about the same things using different words.


I disagree - I think we're talking about two competing philosophies.

DHH / standard-Rails seems to encourage you to fit things into Models, Views or Controllers. Models got too fat, and so Concerns were introduced. This is problematic, because you still have massive god-models, but now their code is split between a dozen different files, called "Concerns". The main problem is that by making ActiveRecord models the core of your application, you generally give each model too much responsibility & too much knowledge of its associated objects. This means it's very hard to break down & recompose the different functionality into new objects. A model only functions correctly if all of its associated objects are present. This is tight coupling.

The alternative view is to introduce several new single-responsibility objects like Interactors/Service-Objects (and Policies, as here) which should contain the core of your application's business logic. These Service Objects are the opposite of concerns - they do not extend the API of models. They exist to control the interactions between the models. The models become dumb interfaces that deal with data validation and persistence. Because they know hardly anything about the other objects in the system, and have very limited APIs, they can be passed around easily, and re-composed into more complex interactions. They are also incredibly simple to test. The same applies to Interactors - they should be broken down into small units of behaviour with very limited APIs, which can then be passed around easily and composed into more complex interactions. This is loose coupling.


See, I still genuinely think we're saying the same things! We're just going about it in different ways.

Rails encourages you to use the "primitives" it ships with, because they've all been more or less designed to work together, and I think this is what DHH is getting at - you get a lot of free behaviour, and in his mind you need a rather strong reason to be throwing them away.

>This is problematic, because you still have massive god-models, but now their code is split between a dozen different files, called "Concerns".

When you put it that way, that still sounds identical to your policies and interactors - you've just chosen a different nomenclature.

Both your approach and concerns are reactions to the way we've been shoving "responsibilities" into our domain objects. When we talk about coupling, we're talking about our ability to reason about our business logic in isolation; generally, the fewer atomic items we have to think about, the easier it is to maintain.

Concerns are just a DSL for writing Ruby mixins; I recently upgraded an older app and discovered I could port it all over by renaming my app/modules folder to app/concerns and I was basically done. So, the differentiating aspect rests in your preferred mental model for breaking up the business logic into these "atomic units".

When you talk about disliking AR models, to my ears it sounds like what you're really saying is "I hate the overhead of hitting the database when I'm writing tests", which is a different idea altogether from "these semantic units ought to only be responsible for serialization".

To me, it's not necessarily relevant if this one object can be persisted; what matters is that it's semantically relevant in my model of the domain/business/etc.

So, at the end of the day there is a ton of logic that is more meaningful when associated with my "users"; but it's an enormous pain in the ass to have huge swaths of my User model governing totally different bits of unrelated functionality. In this circumstance, splitting off the different unrelated bits of functionality is a net win, from both testing and maintenance.

Meanwhile, if we find that there is no good place for stuffing this bit of functionality… then we're probably missing a whole new domain model and we didn't even know it, and imho we're better off encapsulating those behaviours into an independent model.

This is to say: policies as presented in your post strike me as a more convoluted way to achieve this separation of concerns already afford to us by regular ruby mixins/classes.


I agree. I'd go so far as to call the non-"Classic" school the "OO Experienced" school — people who've had copious experience in SOLID OO development in Ruby and/or other languages, who are generally horrified by many aspects of The Rails Way, but find several of the tools and components useful. The apps developed by this school generally transition fairly quickly from The Rails Way apps, to apps that use Rails, to apps that use several components that are also used by Rails but with increasingly strict separation between the AR persistence/validation/nothing else layer and the domain model. The farther along that arc an app progresses, the more survivable it generally becomes for the team and the more flexible (and profitable) a tool for the organisation.

The Rails Way is fine for a prototype, or for an app that is so well-understood that the team know from the beginning will have sharply limited change in certain very-well-known areas. It's safe to say that the vast majority of the apps I've seen in my career do not fall into that category.


But this isn't a failure of "DHH/Rails" philosphy, it's inherent in the MVC paradigm. If we're going to "blame" something, we should be a bit more careful about where that blame actually lies.

The disparity here is between the MVC paradigm and these "emergent" groups being discussed.

The problem is that they don't have a coherent solution. Rather, there are 100 little "solutions" running around with no central philosophy behind them. That's a Bad Thing.


Similar to http://words.steveklabnik.com/rails-has-two-default-stacks ?


Actually I disagree in a big way. I think it's a mistake to conflate choices in tooling like HAML/HTML & mySQL/pg with architecture choices.

Your architecture choices change the way you write your application code and how you utilize the Rails framework. On the other hand HAML is not going to change the way you write markup, it just provides a different syntax that some people prefer. Postgres has features people like that mySQL lacks. MiniTest & Rspec are different syntaxes for testing. These three choices have very little to do with how you use Rails & write your app code, they just happen to be made by the same people who advocate big architecture changes.

I fall in a camp where I use HAML, postgres & Rspec but I also use fat models and skinny controllers. I have a few really small services in my apps but I try to reserve them for cases when the standard Rails pattern is extremely ugly rather than actively seeking out replacements for the Rails way.


Ha! Yes. Exactly so.


I agree that Rails is polarizing into two schools - Steve Klabnik's blog post details this really well.

But I don't think that moving towards more single-responsibility objects means you have to give up all that Rails offers - particularly the preference for concise, human-readable APIs and convention-over-configuration. Ie, we don't need tonnes of boiler-plate just because we want to use more objects.

We put this permission check in the controller and not the interactor because it seems like the controller should generally be responsible for authentication & permissioning. Once you get to the interactor, it should just be told "Perform this interaction". Not "check if you can perform it, and then perform it".


As I point out here https://news.ycombinator.com/item?id=7437872 I think Steve Klabnik's argument is actually conflating two phenomenona. One that there are a set of tools/gems that are more conducive to productivity than the Rails defaults (HAML/Postgres/Rspec) and another that some developers find the Rails architecture to be a pain point.

They are overlapping camps but not exactly the same.


Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | DMCA | Apply to YC | Contact