-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Limit use of conditional modifiers to short, simple cases. #738
base: main
Are you sure you want to change the base?
Conversation
1ff54d2
to
45febc1
Compare
ruby/README.md
Outdated
@@ -2,8 +2,12 @@ | |||
|
|||
[Sample 1](sample_1.rb) [Sample 2](sample_2.rb) | |||
|
|||
> [!TIP] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm glad we're modeling this!
a561b20
to
2199c6f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you!!!
Co-authored-by: Richard Newman <richard.newman@thoughtbot.com>
2199c6f
to
0f20d49
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The examples you give all seem to be pre-conditions and invariants. Maybe we just want allow this style for guard clauses? How do we feel about inline conditions in the middle of a method body?
## Complex conditions | ||
|
||
However, if the line is too long (around 80 characters) or complex (e.g., an | ||
`if` with multiple conditions like `if a && b`) prefer the multi-line form: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is especially true with unless
since you an introduce a DeMorgan bug
## An opportunity to refactor | ||
|
||
If the conditions are related, consider extracting a method that groups them. | ||
This might allow you to use the conditional modifier form again. | ||
|
||
```ruby | ||
def inactive_user? | ||
signed_in? && !current_user.active? | ||
end | ||
|
||
block_access! if inactive_user? | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to be moving towards the Triangle of Separation style of code
## Conditional modifiers feel informal | ||
|
||
The modifier form of conditionals can feel more casual than the multi-line form. | ||
Conversely, the multi-line form _draws attention_ to the conditional and the | ||
code that follows it. Use this to your advantage when you want to emphasize the | ||
conditional and the code that follows it. | ||
|
||
```rb | ||
# Avoid | ||
def action | ||
return destroy_all if really? | ||
|
||
do_nothing | ||
end | ||
|
||
# Prefer | ||
def action | ||
if really? | ||
destroy_all | ||
else | ||
do_nothing | ||
end | ||
end | ||
``` | ||
|
||
You can also refactor the code so the less destructive action uses a conditional | ||
modifier, which pairs well with the informal feel of the modifier form: | ||
|
||
```rb | ||
def action | ||
return do_nothing if chill? | ||
|
||
destroy_all | ||
end | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this section is really about what is the "main action" of the method, and what is the edge-case blocked by the guard close. It seems less about formality and more about verifying invariants?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd say both conditions can be equally valid, which is highlighted in the if/else example? The writer has the power to bring focus to particular branch by not using the guard. Maybe that's what I was trying to communicate with formal/informal.
If we allow inline conditions in the middle of the body, how do we feel about the style that attempts to "flatten" a conditional to fewer lines like: def can_edit_admin_post?
return owner? unless admin?
true
end that often ends up more complex than the explicit if/else. The example above is re-implementing boolean def can_edit_admin_post?
owner? || admin?
end https://thoughtbot.com/blog/back-to-basics-booleans#multiple-variables |
This guideline intersects with some other topics too:
|
@JoelQ it seems to me that inside a method body those would behave the same? def brew_coffee
boil_water
add_coffee_grounds
pour_water
stir
add_sugar if sugar?
add_cream if cream?
serve
end |
- Use [standard] | ||
- Avoid conditional modifiers (lines that end with conditionals). [36491dbb9] | ||
- [Limit use of conditional modifiers to short, simple cases.](./conditional_modifiers.md) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: I think the file name should match the link label
- when they match, it lowers cognitive cost of coming up with both the title and filename
- it's easier to locate the correct file when the filename matches the title
- using the title as the filename is more unambiguous and natural to use.
- would facilitate consistency in naming files
- The file name is more likely to be unique, making it easier to generate links in markdown editors like Obsidian automatically suggest pages to link to
- Differentiates between other files that may also speak about conditional modifiers
- This is similar to what happens on Wikis where the title and file name are the same
- Using a propositional title (at least for those notes where the title forms a sentence or independent clause; such as statements, assertions, declaratives, advice, claims, theories, and/or imperatives) is useful for forming interconnections between pages because the filename/title forms a concept handle. Concept handles can then serve as an abstraction of the page/note itself. Which then facilitates composing and mixing concepts together to generate new ideas.
- I would expect the title "Conditional Modifier" (a lexical title) (singular not plural on purpose) to be a page about the concept or object itself. I'd expect this noun phrase to be a definition or encyclopedic knowledge on what a conditional modifier is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd be interested in finding a standardized layout, form, or pattern (informal definition) for guideline pages. Something akin to Pattern Forms. Though I acknowledge that we aren't generating formal patterns here, but more sharing best practices, maxims, preferences, conventions, heuristics, lessons learned, etc.
Still, I would argue that creating a structural consistency across guideline pages would facilitate the readability, scanability, searchability, navigability, and organization of the pages. This would encourage internal consistency across a diverse set of guidelines.
I'm not sure what this form would look like (we might need to build out several pages first and discover the common pattern). Perhaps something like: problem, solution, tradeoffs (indications/contraindications; pros/cons; advantages/disadvantages).
I could also see us using a variant the portland form as well...
- opening statement — roughly cover the description, problem, context, and forces to be resolved
- therefore... — thesis, solution, the action to take, resolution, resulting context
- but... — antithesis, contraindications, exceptions, things that contrast the concepts already mentioned
It enacts just enough structure to assist with pages being consistent while still being ad hoc and flexible.
In practice, that ending I do occasionally write an interrogative function through with def can_proceed?
return false unless admin?
active? && configured?
end That of course could readily be instead written as That wasn't the point of your question. We're not saying please use guard statements where you can, but that they can be helpful and, given the scope of this particular line in the guide ("Limit use..."), they are an appropriate place to use a conditional modifier than the full form. Also, I was trained earlier by default Rubocop to always put a blank line after a single guard statement (or after the last guard statement in a series of them), so I would have wanted a blank line after |
This PR changes the guideline of "Avoiding conditional modifiers", which I believe lacked nuance.
This was product of a group discussion, and we thought it was good to introduce our reasoning as part of the guideline. That way, there's a new markdown file with the reasoning and examples. This might be a new pattern for the guides.