Ruby and Postel’s Law
Looking at applications of the Robustness Principle to our code.
While watching a talk on Rails as a SOA Client, I came across a reference to Postel’s Law. I’ve been thinking about how to cope with SOA systems changing over time, and Pete Hodgson contends that this is how you do it. So I wanted to take a minute to look at it and see if we can apply it to some simple cases.
For your reference, this is the law:
“Be conservative in what you send, be liberal in what you accept.”
Consider for a moment this snippet of code:
Pretty simple. It accepts a few parameters and applies them to an object. I see this sort of code all day. But this code violates our law in a few ways, so let’s see if we can fix that.
Be Conservative In What You Send
First, let’s look at the return part of the method. How many return types does it have? I see three: a single Thought object, an array, and nil. With so many return types, callers of this code have to be very careful. We can think of a hypothetical implementer working with our code:
This caller can’t be very confident about its own usage, because we don’t know what we’re going to get back. We’ll start correcting that by collapsing some of our cases.
Now we cast our second argument into an array if it wasn’t already one. This will remove our single-object case, so we can be a little more confident already. What about the nil? Well, I’d argue that nil is not a meaningful return value here, so I think we should substitute it for an ArgumentError instead:
Now that we have just a single return type, we are consistent at all times with our return. On the other hand, we’re still picky with our incoming arguments, so let’s look at that next.
Be Liberal in What You Accept
Our method as written is not very forgiving about what it accepts. Checks on the class of an argument are a code smell here, and we should be able to remove them both. First, it turns out there’s a really neat casting method on Kernel called Array():
If you’re like me the first time I encountered this method, your reaction at this point should be something like “what the hell is that”? It looks like a class constant, but it’s really a method. In the global space. Yuck. It turns out, though, that this is actually a pretty normal pattern once you’ve seen it. Many of the Ruby core classes have similar functions:
So what do they do? I think of them as “emphatic casters”. That is, they try really hard to cast your inputs to a target output type:
So! Returning to our refactored method, we can now accept any array-like parameter, a single parameter, or even nil without skipping a beat. That’s a fantastic improvement, and leaves us with one other argument. Right now we’re checking for the Philosopher class. Let’s use our new-found powers to create an emphatic casting method of our own:
There’s our final refactored version of the method. Funnily enough, it isn’t even shorter. So why go to all the trouble? Well, we’ve defined an interface (the to_philosopher method) for defining new types of thinkers, so we’re more closely following the Open/Closed principle. We have confident inputs and outputs to this method. And at least personally, I find this version to be much more readable than the original.
Remember our original caller implementation?
Now code surrounding our method is more confident and easily expressed. That’s the beauty of Postel’s Law. This sort of code is infectious. Try it out in your own projects and see the difference.