How are you tracking Rubocop TODOs for your packs?

This message was imported from the Ruby/Rails Modularity Slack server. Find more info in the import thread.

Message originally sent by slack user U70NN25TNJA

Hey folks! :wave: We (<@U72QH3HFB69> + <@U72QT6YUPFK>) are curious about how y’all are tracking Rubocop TODOs for your packs. Are you recording them in the application-level .rubocop_todo.yml in the root of your project, or are you recording them at the level of packs themselves?

We’ve experimented with adding a .rubocop.yml file to a pack directory that has the following config:

inherit_from:
  - ../../.rubocop.yml
  - .rubocop_todo.yml

That pulls the application-level rubocop config (and the application-level todos), but also includes those defined in the pack itself. We’re looking to strike a balance between being able to make a pack’s rubocop issues legible, and have the tooling feel straightforward. We’re interested as to how others in the community have dealt with this… :pray:

At Gusto we store all TODOs and the config itself in the top-level. We experimented with package level config and TODOs, but ended up reverting that experiment. I think the idea makes a lot of sense in theory, but part of the issue is just that the rubocop ecosystem doesn’t have a ton of support for this pattern, so it can cause a lot of little pains. Here are some things that can go wrong:

• Rubocop TODO generation doesn’t respect input paths and always regenerates the whole thing. We maintained some custom code to allow regeneration of TODOs on a per-pack level. It’s actually not too hard to do. Here’s some starter code that allows incremental regeneration of the monolithic TODO. The same idea can apply to per-pack TODOs. Ideally, this could be improved in upstream rubocop.
• When you have a new .rubocop.yml, all configured paths are relative to that config file. That includes for inherited files – so the paths in your root .rubocop.yml are relative to the pack itself. This may not be a problem (since packs look like tiny rails apps), but could be confusing.
• Generally speaking, the use case for a rubocop cop is we want the whole codebase to conform to a pattern. We thought that some cops might be only relevant to specific paths on an opt-in basis, so we tried the per-pack config. In practice, we generally wanted everything to conform to a pattern, and it’s easy and familiar for folks to opt in/out in the root .rubocop.yml , so this didn’t end up really solving a real problem for us.
• We only allowed specific cop exclusions to show up in the pack-level TODO, the rest in the monolithic TODO. This was pretty confusing and I’d probably choose one or the other.s
• It encourages folks to diverge from the org’s choice on code style by creating a nice place for folks to change rules just for their pack, which is potentially not desired.

In theory, each pack being responsible for its own TODO places the pain/responsibility of a rubocop TODO closer to the owners. I’m not sure though if these incentives are actually created in practice though.

One thing we found at Gusto is that cops are either never fixed (they just gradually get fixed as code is moved around) OR they’re auto-corrected by someone feeling like they want some zen task. Also not sure if this would change with per-pack TODOs, or even if the ideal/most efficient use of time is for folks to fix issues in piece meal vs a larger sweeping effort.

Message originally sent by slack user U70NN25TNJA

Thank you so much @AlexEvanczuk - this is super helpful context, and we’re ever grateful for your help!

It’s really interesting hearing the theory vs. reality that played at Gusto here. On one hand, having everything neatly bundled up within a pack seems like the right thing, but as you say, the reality can be quite different - classic sociotechnical (and tooling) problems :slightly_smiling_face:

It’s also interesting hearing that the cops were kind of ambiently fixed, rather than directly paid down. Lots to think about there too.

Thanks again!!