Personal blog

Boolean arguments in Ruby vs clean code

In his “Clean code” Uncle Bob univocally condemns boolean arguments.

Boolean arguments loudly declare that the function does more than one thing. They are confusing and should be eliminated.

To some extent, it makes all the sense

Take a look at this method:

def articles_base(only_published)
  return Article.published if only_published
  Article
end

Easy to read, right? Now try to forget it, and imagine yourself being a client of that method. You can call the method in two possible ways:

articles_base(true)
articles_base(false)

Is everything clear from this perspective?

For me, it’s certainly not. A rather important from clean code perspective question arise:

  • What the heck is the argument?

A followup question is: how to prevent developers from never confusing true with false?

Unfortunately, you have to remember the implementation when calling the method. But it’s not the point of extracting methods! Once extracted, the method should clearly communicate what it does!

In other words, articles_base is an example of a leaky abstraction.

Instead, Uncle says, there should be two independent methods, even if they share some functionality. This is how those methods could look like:

def articles_base
  Article
end

def published_articles_base
  Article.published
end

Now when you call it, there is no doubt about the meaning of an argument, because the is no argument at all.

I agree. Boolean arguments are bad… in Java.

We can do better in Ruby

We can leverage keyword arguments to dub the arguments. For example, instead of this:

mock_authy(true)
mock_authy(false)

we can do this:

mock_authy(success: true)
mock_authy(success: false)

The upside is obvious – you know the meaning of that boolean argument.

But not only that. Aesthetically, and from the clean code standpoint, I probably like that approach over this one:

mock_authy_success
mock_authy_fail

The keyword arguments way indicates that the methods have something in common. In fact, if the difference is minor (unlike the articles_base example above), I would push it aside, to the argument, rather than create a separate method. Another thing to consider – if the number of arguments ever grew, the caller wouldn’t have to worry about their order.

As a Rubyist, clean coder, and “context is king” approach fan (that is, everything depends), my thesis is:

  • there’s some space for mock_authy(success: true)
  • there’s some space for mock_authy_success
  • there’s no space for mock_authy(true). Use keywords argument instead.

Some understanding why to use which Martin Fowler explained in his FlagArgument post. The last paragraph advocates the idea of boolean arguments in some so-called “boolean setting method”.

Aside

Because of duck-typing (meaning no static type checking), there’s no concept of boolean argument in Ruby. There’s only hope :). No compilator stops you from calling mock_authy(success: User.all) .

There are ways to deal with this caveat. Mostly, however, a decent test coverage and being clean in the code should suffice.

Takeaway

As much as I agree with Uncle Bob in many ways, I think he explains the idea of the clean code for Java and similar languages. Ruby is different. Some rules don’t entirely apply to our ecosystem.

2 Comments

  1. Yevhen V.

    Thank you for sharing thoughts 🙂

    So, what does mock_authy(success: false) do, what it returns and how result is used?

    Would it be possible to show how “ruby is different” with the same example Uncle Bob used to explain this idea? I found current examples a bit confusing, the first one seems to having the same clarity problem even with keyword argument, e.g. ‘articles_base(only_published: false)’ is it “not only published, i.e. all articles” or “only not published”?

    • Bartek

      > So, what does mock_authy(success: false) do, what it returns and how result is used?

      All I remember at this moment is that we had been using Authy (3rd party to handle 2FA), and in order not to make a real HTTP request, we had to mock the request. `mock_authy` was a wrapper doing just that, responding with either success or failure, based on the argument.

      > Would it be possible to show how “ruby is different” with the same example Uncle Bob used to explain this idea?

      I would certainly try to, but I don’t have the book at hand, and I don’t think the example is available online. Unless you have it somewhere…?

      > I found current examples a bit confusing

      Gotcha. Well, I’m only saying that `articles_base(only_published: false)` is a bit clearer than `articles_base(false)`. At least the author has a chance to give “a name” to that (otherwise definitely illegible) boolean value. Sometimes both ways are confusing, because the rest of the code is badly designed (the example is a real-life example from Redmine, afair)

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

© 2024 Bartosz Krajka

Theme by Anders NorenUp ↑