Helping devs understand concerns faster

I think we’d do well to explain concerns better. I’ve been writing code with mixins for so long that I’ve taken it completely for granted that this is a wonderful way to structure separate domain concerns.

But I also agree that concerns do not warrant a generator. A lot of concerns are simply Ruby modules that are mixed in, with no additional stuff added. And then for a few cases, like when your concern includes both class and instance methods, or you want to call class methods on mixin, you can use extend ActiveSupport::Concern.

I’d also just put my two cents here that I’m actually not that big a fan of Concerning. That is adding inline concerns in a class. We don’t use that at Basecamp any more. It was an approach introduced by @bitsweat, so I’ll let him speak to what he thinks about it.

But I went over the way we use concerns at Basecamp at length in that video that was linked above. Here’s another example from HEY:

class Identity < ApplicationRecord
  include Accessor, Authenticable, Boxes, ClearancePreapproval, Clipper, Creator, Eventable,
    Examiner, Filer, Member, PasswordReset, Poster, Reaching, Regional, TopicMerger
end

module Identity::Accessor
  def accessible_topics
    Topic.joins(:accesses).where(accesses: { contact: contacts })
  end

  def accessible_entries
    Entry.joins(:topic).merge(accessible_topics)
  end
end

As you can see, we often put as little as 1-2 methods into a concern that encapsulates that specific concern. The way you reach accessible objects. These objects absolutely could just have been declared in Identity, but add all those concerns up we have in Identity, and you’ll see that Identity would be a big basket of dozens of methods. Sure, all these methods are about Identity, but there’s a grouping below that. That’s what concerns are: A way to group related methods together, while still living under a single class.

You can think of it like this: The class is an H1, the concern is a H2, the public method is an H3, and private methods could play H4/p. It’s a way to structure a large argument. Sit down, and lemme tell you about the Identity class! Let’s start by talking about how the Identity acts as an Accessor, and you should reach what that identity can see as an accessor through the accessible_* methods declared in the Accessor role.

It’s structured writing.

17 Likes

Worth pointing out here is that the stuff we put in app/models/concerns are concerns that apply to multiple classes. An example from HEY is eventable:

module Eventable
  extend ActiveSupport::Concern

  included do
    has_many :events, as: :eventable, dependent: :destroy
...

class Access < ApplicationRecord
  include Eventable

class Clip < ApplicationRecord
  include Eventable

But the majority of our concerns are actually like those presented for the identity: Just a way of breaking up a large argument, like Identity, into smaller ones, like Accessor.

Now I understand that argument from people who say: well, Accessor should just be it’s own class! Yes, it could have been. You could do Accessor.new(identity).accessible_topics. But I find that style awkward. The fact that the identity is playing the role of an accessor is implicit in #accessible_topics.

So that’s another way of putting this: It’s a writing style. Like using subheads to explain subservient ideas within a broader context. You could probably extract all those subheads out, and turn them into little essays of their own. But that’s often just not the right level of extraction. As you saw, the Accessor role starts life as literally just two methods! It doesn’t warrant being turned into it’s own class. It doesn’t have an independent center of gravity.

As you’ll probably gather from all the writing metaphors, this isn’t a technical debate in the sense of “When x, then do y”. It’s a discussion of writing styles. Subtle breaking points. Careful evaluations.

But at the same time, you can absolutely teach style. A Rails Strunk & White would be great :smile:

19 Likes

I think that we can do more work in the guides on the getting started section. Current getting started demo app is ok but it is extremely bare-bones that just shows how easy is to start with Rails. We could expand a little(not too much) on that app and show how concerns can be used and other stuff too.

We could expand that section to introduce:

  • Sessions - login/logout
  • Refactoring with concerns
  • Tests and Fixtures
  • A dash more of Turbolinks and webpack
  • Maybe also add Mailers and background jobs
  • Maybe even deployment

I had a pleasure in my life to coach a few young devs that were new to Rails. I couldn’t accomplish a good introduction to Rails with the guides, we needed to use other paid courses/resources just for a basic introduction. But, if we expand a bit on the getting started demo app I feel that can be the best starting point.

6 Likes

When you said this, it made me think of the Securing Rails Applications guide. Not quite the same but something to get to some inspiration from perhaps.

1 Like

I had no idea they were a thing until I watched those videos.

1 Like

There is a demo app? Cool, I never noticed such a thing existed, when I started out, I always loved to see examples of how things were done.

I think something that often gets missed regarding concerns is that they’re basically just syntactic sugar on top of standard Ruby mixins, and the problem there is that some other popular programming languages have little to no proper support for mixins. (I’m looking at you, JS! But some do, like Protocols in Swift/Objective-C.)

I personally am a massive fan of mixins/concerns, and I think they would be better regarded if the education piece started at a more fundamental level like “Why Ruby Has Mixins and Why That’s Awesome” and compared what they provide developers vs. non-Ruby-esque programming techniques.

5 Likes

Also: in my experience a lot of non-ruby coders are required to work on rails code bases. They will never dig deep into ruby and just make code, that is java-esque and have all the popular patterns. But it will be bad Ruby code. And it will have zero mixins as their primary languages don’t support mixins.

1 Like

I just wanna echo the idea of writing a guide or set of guides to convey the ideas that DHH’s expressed here and in his screencasts. Not just about how to use concerns/mixins on a technical level, but about how to use them philosophically.

The omakase can extend to “tried and true” patterns and pathways that newbies can look to for advice on setting up their new (read: maybe not even first) app. The starting point for a guide could literally be what DHH just posted in terms of content and that would be a 10x improvement over what we have.

4 Likes

There is getting started app example here. So what I’m proposing is to expend this “getting started” section and showcase how other parts(including concerns) can be used in the context of real application.

For example on thing could be how controller concerns could be used on the scoped controllers:

class Posts::CommentsController < ApplicationController
  include PostScoped
  
  ...
end

class Posts::ParticipantsController < ApplicationController
  include PostScoped
  
  ...
end

module PostScoped
  extend ActiveSupport::Concern

  included do
    before_action :set_post
  end

  def set_post
    @post = Post.find(params[:post_id])
  end
end
1 Like

I understand that Concerning might seem to fall in the valley between useless and incorrectly placed.

Let me go off on a tangent and make my case for it: it’s a good temporary, low-overhead tool for chunking of concerns. It’s “good enough for now” instead of “perfect or nothing”.

It allows me to group methods together while I’m in context and express “this all goes with the same chunk” without needing to break out into another file, typing three boilerplate lines of code. It’s communicative.

In the same spirit as the H1-H2- model, I can just markup my H2 here and there and keep writing instead of needing to declare-extend-include. If I reorganize later and learn new things about the future, it’s easily undone without worrying I’ll break something. The code works exactly the same, but what it communicates to humans changes.

I don’t think long term you’d want a component with a bunch of concernings laying around. But taking that first step of chunking while you have the context in mind and the future complexity is unknown is super useful. I can come back another day, when my concern has grown large, and take the time to place it into its own file. Or I could have learned that this didn’t complexify as expected, and remove the concerning boundary. It’s a lean, iterative tool to me.

If I may make a gardening metaphor (I’m propagating pothos’ these days):

Concerning is the water jar I put my cuttings in to grow roots. Maybe it will, then I’ll transfer it to its own pot – or maybe it won’t and a pot wasn’t necessary – in which case I saved the effort.

4 Likes

On the one hand I am also unsure if a Concern / mixin tutorial is needed in the Rails guides. On the other hand, I’ve seen many projects were Concerns have just been used to extract parts of a god object into other files to make rubocop happy. And this is a code smell, which can lead to hard to understand and maintain code. So yes, maybe there is some more education on this needed.

sighh, if only these were in text format instead!

well, clearly then that’s the problem. sounds something like coding “for” the linter instead of coding “with” the linter. i feel like this is a common problem accross programmers and not just something about Concerns, so even with extended education i don’t think that sort of issue would go away.

@msapka i’ve seen this loads of times first hand. maybe exposing mixins very very visibly in rails code at the guides or something would help? i’m not sure

For then Ruby is often Rails, so having it in guide would be very useful

1 Like

Totally agree with the concern generator. So much that we wrote one for ourselves, which later ‘evolved’ to a sublime text snippet, since we use it a lot.

But a guide with patterns for using concerns would be great. DHH also pointed out that there are two kind of ‘concerns’ they use, one is just for organizing code (that will not be shared with another classes, so it’s just tidying up) and the regular concerns that you use to share code.

My personal experience with Concerns is that they are headache. I find them to be extremely dangerous and easy to make a disaster with them. I’m aware of Provide sharp knives and I’m cool with that. But I find it really important that we as a community tell people how to use those sharp knives, and how to not use them. For example:

  • Should a concern know about the details of the model/class including it (e.g. a column name)?
  • Should a concern know about other concerns?
  • Should a model/class know about a concern’s methods?
  • Should they be used only to break up large models? Or when else could they be used?
  • etc

Is it something we can do in code to prevent disasters within Rails or is the only thing we can do to add better and more documentation?

I’m not trying to convince anyone but here I share some links about the disadvantages of mixins/concerns so that we are all in the same page:

BTW I’m open to give another chance to Concerns if there is a doc where I can point people when I see them being used incorrectly.

Thanks

1 Like

My two cents: concerns aspire to be independent modules. Meaning, they could be included or not in a certain class, and nothing would break. They are plug in. I always think “okay, what if this module wasn’t there. Okay, what if it was.” To try to make a clean interface.

Of course, as any piece of code, it can have dependencies. Depend on another module, another class, something being present in the included class, name it. To me this is a bit a question of software engineering in the abstract. Single responsibility principle, what’s public interface what’s private, dependency graph, and so on.

Second, to me Concern is an invaluable tool to get there “eventually”. I wouldn’t be a happy camper if I had a linter go “your concern is not perfect (perfectly independent). Please do more work because this gradual progress is unacceptable.” This sounds very dramatic, but I’ve seen tooling from JS land that has just horrified me, so I’m never too careful.

Hopefully this helps a bit.

2 Likes

I agree with this. From my experience when I see newly created concerns they seem pretty good, the problem is with their maintenance/extensibility. Soon there will be a requirement that will introduce bi-direction between model and concern or someone coupling N concerns together if not being really careful/having experience.

I think the main problem is that Rails “suggests” to use Concerns by creating the app/models/concerns and app/controllers/concerns folders by default but then it doesn’t give any advice on its intended usage. And as I already mentioned I find them to be extremely dangerous. I’m trying to put myself in the shoes of a junior dev, let’s help them have a good experience in Rails and not end up with a mess because of concerns.

If we can help by not allowing anti-patterns then that’d be amazing (or maybe we can do this in Rubocop), but yes, the least that we can do is provide a document talking about them and providing best and bad practices…

1 Like

It sounds like you have perspective on this that might be helpful to express in a blog post.

2 Likes