I originally learned a lot of these ideas from Confident Ruby, one of my favorite Ruby books. If you like this post, you should buy it and read the entire thing. There’s so much good stuff in there.
Your current_user
method returns a User
, except when there is no user and it returns nil. A search
method returns an Array
of results, unless there’s only one result, and it returns just that result instead. Seems reasonable, right? Maybe even convenient!
But soon, these decisions will bury your code under a mountain of if statements. Maybe it’s a bunch of if kind_of?
sprinkled all over. Or maybe you feel like you have to check for nil everywhere. Or worse, NoMethodError
s start showing up whenever you ship a new feature. Guess it’s time for another hotfix!
There is a way to prevent this, though, and all it takes is a little thoughtfulness.
The Robustness Principle
There’s a principle in computing that says,
Be conservative in what you do, be liberal in what you accept from others.
You can apply this principle to your Ruby methods. The methods you write should accept reasonable input, and should return consistent output.
Focusing on the last part: When someone calls a method you wrote, they should know exactly what that method will return.
Be thoughtful about your output
Take a look at Rails 2.1’s implementation of ActiveRecord::Errors#on
:
When called, this could return either an Array
of String
s, a String
, or nil
. It’s up to the caller to figure out which type of object it’s dealing with. This is a bad idea:
-
The caller has to muddy up its own code with obtrusive type-checking.
-
The caller has to know a lot about the method it’s calling. At a minimum, it needs to know every type of object the method could return and when each type could be returned.
-
You have more edge cases to test. If you want to be confident that your code is doing what it’s supposed to do, you have to try out all three scenarios.
Your methods should be consistent about what they return. If you usually return an Array
, do what you need to do to always return an Array
. If you usually return a User
, but sometimes return nil, you could create a Null User object and return that instead of nil.
You could even be less strict: “I’m going to return something that includes the Taggable
module”. You could go more generic: “I’m going to return something with id
and name
attributes.” The important thing is consistency and making sure your caller knows what to expect.
jQuery is an interesting example. Most jQuery methods return the same kind of Array
-like object. Because of this, jQuery’s methods are incredibly composable, and you can do crazy amounts of work in a single line of code.
And in case you were wondering, Rails fixed that method in later versions:
Now it always returns an Array
. Simpler for them, and simpler for us.
Kill inconsistency
The next time you find yourself returning “An Array
or nil
”, just return an Array
. Take a look through your codebase and see where you’re using kind_of?
and respond_to?
. See if you can refactor the methods called by that code to return a single type.
And watch as the assumptions you can make about your return values ripple through your project and simplify all of your nearby code.