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.
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,
NoMethodErrors 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
When called, this could return either an
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
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.
The next time you find yourself returning “An
nil”, just return an
Array. Take a look through your codebase and see where you’re using
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.