Skip to content
Open
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions 2025-06-02-default-reek-configuration.md
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would it be possible to propose a new rule? if so, where is the best place to do it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comments here in the PR is the place for that

Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Default Reek Configuration

## The Problem

We like to use Reek in many of our projects as one of the tools to aid in improving the code quality, but we do not have a clear expected way to use Reek in the different projects we use and we have no clear guidelines about when to make exception or changes to its configuration.

## Proposed initial Reek configuration

This RFC is a proposal of a default Reek configuration file that we would use in established projects to reduce the number of warnings and the need of exceptions that we see with the [default configuration](https://github.com/troessner/reek/blob/a8649370ced77dd0e798b00dc97ff916f68bb002/docs/defaults.reek.yml), and to also define some guideline or expectation on how we can decide when to add exceptions and modifications for different needs.

### Default .reek configuration file

This file is meant to be added in the root of the project as `.reek.yml` to handle common cases of Reek warnings that will want either disabled or reconfigured based on what we've seen in many projects.

It's a combination of the [official `rails-friendly` configuration recommended in the Reek's README](https://github.com/troessner/reek?tab=readme-ov-file#working-with-rails) and some changes to the default rules.

```yml
detectors:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is useful too.
It allows us to do rescue e => and map { |x| x.do_something }

  UncommunicativeVariableName:
    accept:
     - e
     - x

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sounds good

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should also add _ for those times we have a unused variable?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added these 3 accepted short names

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should add k and v as they are common short names for key and value when iterating over a hash

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good to me

IrresponsibleModule:
enabled: false # we don't really add module descriptions ever
TooManyStatements:
max_statements: 10 # original is 5
DuplicateMethodCall:
max_calls: 3 # original is 1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this enough for tests?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do you mean?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I am wrong, but from what I remember the duplicate method calls happen often in tests when you are calling a method several times??

Copy link
Contributor Author

@arielj arielj Sep 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just tried calling a method multiple times in a single test and reek doesn't complain about it

I'm not sure how it works internally but it seems it won't complain during tests? we can always change this later to disable it for the test and spec directories I guess if it starts creating issues (maybe there's some edge case I don't know how to trigger)

NilCheck:
enabled: false # in some projects were this was enabled, it was a false positive being ignored
UnusedPrivateMethod:
enabled: true # default is false, we are still disabling it for controllers and models below
UtilityFunction:
public_methods_only: true # original is false

directories: # reek's recommendation for Rails applications unless a comment is added
"app/controllers":
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the past I have found the following helpful in controllers too:

    FeatureEnvy:
      enabled: false
    TooManyMethods:
      enabled: false
    TooManyStatements:
      enabled: false

FeatureEnvy: It is easy for controller methods to interact with other parts of the application
TooManyMethods: Controllers can easily go over 15 methods
TooManyStatements: Controllers methods easily go over 5 lines

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you think those are better fully disabled or maybe add as exceptions when needed?

I'm running reek on fastruby with the current state of this proposed config and I don't get any TooManyMethods nor FeatureEnvy warnings for controllers, and only 2 TooManyStatements in one of the controllers (maybe those actions can be ignored as exceptions or refactored)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we can disable TooManyMethods for controllers and models too? I don't think it adds that much value (I think we are already good at not putting methods where they don't belong, and when they are in the right place it would be annoying to have reek complain just because of the number)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am onboard with disabling TooManyMethods in controllers/models yes.
And perhaps you are right, we can disable feature envy and too many statements where needed.

NestedIterators:
max_allowed_nesting: 2
UnusedPrivateMethod:
enabled: false
Comment on lines +41 to +42
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if it wouldn't be better to have it be true. Even in controllers and models, if it's unused, wouldn't we want to remove them?

Copy link
Contributor Author

@arielj arielj Sep 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the problem with this rule is that (at least from my tests), it's not smart enough to detect that you are referencing a private method in a callback, as in before_action :some_private_method on a controller or an after_save :do_something_private in a model

we have that enabled above in general, but we are disabling them for controllers and models

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it was showing many false positives saying a private method was not used when it was used in a callback

InstanceVariableAssumption:
enabled: false
TooManyInstanceVariables:
enabled: false # instance variables are the way to pass data to views, it's expected
"app/helpers":
UtilityFunction:
enabled: false
"app/mailers":
InstanceVariableAssumption:
enabled: false
"app/models":
InstanceVariableAssumption:
enabled: false
UnusedPrivateMethod:
enabled: false
"db/migrate":
FeatureEnvy:
enabled: false # keeps complaining about `t.string :col_name` as feature envy calling `t` more than `self`
UncommunicativeVariableName:
enabled: false # complains `t` is uncommunicative, but it's a common short name for `table`
TooManyStatements:
enabled: false # long tables require many statements, reek keeps complaining about them
DuplicateMethodCall:
enabled: false # complains about duplicated methods when a migration creates more than 1 table, which cannot be "fixed"
UtilityFunction:
enabled: false # complains about utility functions in generated migrations, it's ok to have utility functions in migrations

exclude_paths:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

- db/migrate In the past I ignored the db/migrate directory too

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we are already disabling many checks for the db/migrate directory above, do you think it's better to just ignore everything instead?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if there is much value in reeking db/migrate file at all.
They typically fall outside of the normal application - and should not really contain business logic at all.
So I would say yes, let's disable all of the rules in this directory.

- node_modules # in case any node package has a .rb file (like the styleguide)
- vendor # in case gems are bundle inside the project's folder vendor folder
```

### Exceptions and :reek:\* Comments

When applying this configuration, projects will still need exceptions in some cases where the code smell is not flagging code we want to change or when there's a warning on code that's in the same file but out of the scope of the work done.

It is expected that the team working on a given project will keep an eye when these exceptions are used, and there should be a clear explanation to skip a code smell instead of fixing it (which is a valid thing to do, but shouldn't be done just in order to not address it).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'd like some examples of this. I don't know of any off the top of my head, but I'm only thinking that, situationally, if a smell requires a fix that we know the client doesn't really care about or that, in our evaluation, isn't worth the time out of the project it would take to fix it, then we could skip it.

Is that it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not for client project, it's only for our internal projects

and example here will be that with these defaults, when we run reek in fastruby.io/ombulabs.com/points we will have reek code smells, if something reek complains about is not worth the time to fix it (maybe a method is long because it has to be long, or maybe it makes total sense to have a utility function in a file and moving it somewhere else would make it less readable), then we can add reek comments to skip it

Copy link
Contributor Author

@arielj arielj Sep 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

another example could be a private method in some class that is not used explicitly but because it's used by some send(...) call with variables, in that cases reek wouldn't know it's actually used

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the main idea for this is to not encourage people to just add :reek: comments because they didn't feel like fixing a reek warning, if something won't be addressed there has to be a good argument for that (good as in the people working on that task/pr/story agree with)

Copy link
Contributor Author

@arielj arielj Sep 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and this can also help use improve these defaults, if we see something that gets skipped a lot, we might be able to change these defaults based on those reasons, but we need a reason


## How is this going to impact our internal teams and their day to day?

In the near future we plan to adapt this configuration in these projects:

- FastRuby.io
- OmbuLabs.com
- Points

Other project will not adapt this configuration. We expect Ruby and Rails projects to reach certain maturity before we adapt these standards.

Copy link
Member

@FionaDL FionaDL Sep 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious about this part, while I understand not wanting to slow down development in these smaller, newer projects...at what point do you add this, and how much time does it then take to go back and make sure everything is agreeing with reek standards? Where will the line fall for us?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this is answered below, somewhat, but now I'm wondering at what point do we consider a project is not an MVP/Prototype or a one off internal tool that might be obsolete in a year...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cc @etagwerker @ABizzinotto here, I don't know if we have a clear definition of when this crosses that line (for example, in that list we are not listing UpgradeJS.com)

one thing though is that when we want to add reek to a project, it can be added with TODOs to address things gradually, it doesn't need to be something like "we add reek and now we have to fix all the smells", it's more like "we add reek, and we can address the smells gradually over time along with other changes"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't that break the build though?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we add the reek ignore comments it shouldn't break the builds, it would be useful to flag the comments as something that is agreed to be ignore vs the ones we ignore to deal with them later with a TODO

## What projects are not a good fit for this new standard?

We do NOT want to adapt this standard on these types of projects:

- Prototypes
- Unproven MVPs that might be discarded
- One-off internal tools that might be obsolete in a year

## What this file is NOT

There will never be a single Reek configuration file to match the needs of all projects we have, this file is only a starting point with some known generic settings.

## What we are looking as comments

For this RFC we are expecting comments of different types:

- if you disagree with any of the proposed defaults, let us know
- if you think another setting that is not listed here should have a different config, let us know
- if you think it's not clear when a code smell can be skipped and you have some examples from public projects, we can try to discuss and improve the guidelines on when to skip them with links
- if you think any other common rails directory should have different rules, let us know