Live data from Hacker News

Rails – The Missing Parts – Policies

eng.joingrouper.com

31–40 of 65 posts

Re: Rails – The Missing Parts – Policies

#31
post #26

Earlier quoted context omitted.

It sounds like you're letting "ease of test maintenance" drive the architecture of the rest of your app; I'm inclined to accept that incentivizing test creation will lead to more confidence in the face of changes but am somewhat unconvinced it's 1:1 with "less coupled, more maintainable" a priori. Case in point, I'm really uncomfortable with class names with verbs. Typing CanConfirmTicketPolicy.new(ticket: ticket).al…

> It sounds like you're letting "ease of test maintenance" drive the architecture of the rest of your app; I'm inclined to accept that incentivizing test creation will lead to more confidence in the face of changes but am somewhat unconvinced it's 1:1 with "less coupled, more maintainable" a priori. I've thought of making a similar comment on _many_ other articles but never have. This is actually the first time I've…

Not to dull the shine of my own brilliance but I'm pretty sure I've seen dhh say something along those lines in his previous commentary.

I've spent a lot of time thinking about this subject, and have only recently begun to feel confident enough to speak with some authority on the subject.

I think people aren't thinking critically about their code in quite the right way, despite their blog posts on the topic, but I've hitherto been unable to compose my thoughts outside of comment boxes.

Re: Rails – The Missing Parts – Policies

#32
post #22

Earlier quoted context omitted.

This seems like, in essence, a simple function placed in a domain context. For example, in node.js, I would make this a module (a file that exports a function). The module would be named and nested in a directory structure that matches the domain. It seems that the logical ontology is based on ruby's objects. Unfortunately, this makes the solution much more convoluted than a simple function exported in a module.

The solution you describe is isomorphic to the one proposed in the blog post, which I think you recognize? But both leave a lot to be desired. I personally think in both cases they are a code smell, and we ought to either introduce an object whose explicit job it is to worry about these inter domain interactions or pile it up as a mixin into the appropriate domain models.

The solution I describe is basically a functional approach. The function is responsible for the policy concerns over all data in the system.

The function itself can be decomposed into smaller functions.

If you need extensibility, you could make the function composable and register additional pieces to the policy. Most codebases don't need this sort of extensibility.

Simple constructs & consistent, accurate, precise naming is preferable to complicated architectures.

Re: Rails – The Missing Parts – Policies

#33
post #21

Earlier quoted context omitted.

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/responsibi…

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 co…

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.

Re: Rails – The Missing Parts – Policies

#34
post #22

Earlier quoted context omitted.

The solution you describe is isomorphic to the one proposed in the blog post, which I think you recognize? But both leave a lot to be desired. I personally think in both cases they are a code smell, and we ought to either introduce an object whose explicit job it is to worry about these inter domain interactions or pile it up as a mixin into the appropriate domain models.

The solution I describe is basically a functional approach. The function is responsible for the policy concerns over all data in the system. The function itself can be decomposed into smaller functions. If you need extensibility, you could make the function composable and register additional pieces to the policy. Most codebases don't need this sort of extensibility. Simple constructs & consistent, accurate, precise n…

:%s/function/module/ and no meaning is lost :).

I've yet to catch up on my lisp and fully grok the "functional paradigm" and, admittedly, passing around consts is less elegant than passing around function pointers, but what you're describing sounds isomorphic to me in terms of code organization. Look at the blog post; he's just passing a one method class, which… is basically a less elegant function.

Re: Rails – The Missing Parts – Policies

#35
Ensuring that the Ticket is valid is obviously a domain model concern. Policy objects can be a fine idea when they're swappable and you need to allow for multiple different policies. This is not one of those cases.

Here's a much simpler approach that keeps the validation logic in the domain model and uses features that Rails has had since the dawn of the framework: https://gist.github.com/dhh/9672827

Re: Rails – The Missing Parts – Policies

#36
post #35

Ensuring that the Ticket is valid is obviously a domain model concern. Policy objects can be a fine idea when they're swappable and you need to allow for multiple different policies. This is not one of those cases. Here's a much simpler approach that keeps the validation logic in the domain model and uses features that Rails has had since the dawn of the framework: https://gist.github.com/dhh/9672827

On a separate note, I feel like this article series might better be titled "The Missing Parts of Our Knowledge of Basic Rails Features".

Reinventing basic features doesn't make your Rails deployment "advanced", it just makes it convoluted. There's no bonus prize for introducing fancy patterns to situations that do not call for them.

Further more, here's the definition of the Active Record pattern, as described by Martin Fowler: "An object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data". The key part is that last sentence.

So a quote like what follows simply misunderstands the purpose the Active Record pattern: "A recurring theme in these posts is that ActiveRecord models should be very simple interfaces to a datastore – the User model should be responsible for validating and persisting attributes of a user, like name and date-of-birth, and not much else."

See the example diagram (which I actually drew for Martin back before even starting Rails!): http://www.martinfowler.com/eaaCatalog/activeRecord.html -- it includes domain logic methods like "getExemption", "isFlaggedForAudit", and so forth. Exactly like the pattern describes.

You're not a beautiful and unique snowflake.

Re: Rails – The Missing Parts – Policies

#37
post #36
post #35

Ensuring that the Ticket is valid is obviously a domain model concern. Policy objects can be a fine idea when they're swappable and you need to allow for multiple different policies. This is not one of those cases. Here's a much simpler approach that keeps the validation logic in the domain model and uses features that Rails has had since the dawn of the framework: https://gist.github.com/dhh/9672827

On a separate note, I feel like this article series might better be titled "The Missing Parts of Our Knowledge of Basic Rails Features". Reinventing basic features doesn't make your Rails deployment "advanced", it just makes it convoluted. There's no bonus prize for introducing fancy patterns to situations that do not call for them. Further more, here's the definition of the Active Record pattern, as described by Mar…

>> On a separate note, I feel like this article series might better be titled "The Missing Parts of Our Knowledge of Basic Rails Features".

Agreed on so many level. I see so many new gems that does the exact same thing as what rails do by default. I've spent some times browsing Rails source. It took me a while and as a side effect, the number of gems I am using now is a fraction of what I used when I started.

You can't build libraries and abstraction on top of Rails if you don't understand rails to begin with.

Re: Rails – The Missing Parts – Policies

#38

I would use CanCan ability.rb file for that: https://github.com/CanCanCommunity/cancancan

My problem with CanCan is that when you begin to have more complicated access logic ability.rb becomes a giant mess.

It's already a file where you just throw in all of your authorization logic anyways so it always feels a bit unruly once you get beyond basics.

Re: Rails – The Missing Parts – Policies

#40
post #36
post #35

Ensuring that the Ticket is valid is obviously a domain model concern. Policy objects can be a fine idea when they're swappable and you need to allow for multiple different policies. This is not one of those cases. Here's a much simpler approach that keeps the validation logic in the domain model and uses features that Rails has had since the dawn of the framework: https://gist.github.com/dhh/9672827

On a separate note, I feel like this article series might better be titled "The Missing Parts of Our Knowledge of Basic Rails Features". Reinventing basic features doesn't make your Rails deployment "advanced", it just makes it convoluted. There's no bonus prize for introducing fancy patterns to situations that do not call for them. Further more, here's the definition of the Active Record pattern, as described by Mar…

Thanks for taking a look - I certainly respect your viewpoint.

I guess where we diverge is the amount of domain logic which lives on an ActiveRecord model.

My experience is relatively limited, but in 4 or so years of professional Rails development, across many different codebases, the overwhelming majority of problems have been caused by bloated models with too many responsibilities.

You clearly have more experience with Rails, but your solution always seems to be "If you were smarter, you wouldn't have these problems with my framework". I've seen this problem recur again and again across multiple teams & codebases.

We're seeking to address that problem with these concepts that have been successfully used in other frameworks & languages.

Post reply on HN