Here’s a thing I don’t like: People asking me stuff like
Hey, do you have time on Saturday? while they want to ask
Hey, I’m moving. Can you help me haul all my stuff up and down 22 floors on Saturday? I have no way knowing what they want until I gave an answer (or asked
Now take a look at this snippet of code:
if user.author? || user.admin? show_create_blog_post_button() end
Can you guess what I don’t like about it?
If your answer is that the question hides it’s intent you just earned yourself a cookie. Go on. I’ll wait while you eat.
In all seriousness though, what the code does want to ask is
Is this user allowed to create blog posts?. But what it does ask is:
Is this user an author or an admin?. That is a big difference and makes the lives of the programmers that have to maintain that code a good bit harder.
The single most important aspect of OOP (and probably any form of programming) is intent. If code clearly communicates its intent you will have a much easier time understanding what’s going on.
In our example we are only able to gather the intent of the question from the consequences of the answer. To illustrate what I mean have a look at this:
if user.author? || user.admin? show_blog_post_statistics() end
Still makes sense, right? The question the code asks does not change, while its intent does, though. Obviously now it wants to ask
Is this user allowed to see the statistics?. Since this change of intent is not reflected in the question however, there is no way to derive the intent of the question without understanding the code it guards. Best case: You have to read a few lines before getting back to the
if statement and being able to make a call if it all makes sense. Worst case: you cannot easily figure out what the code inside the
if does and have no way of telling if the whole thing makes sense or not:
if user.purple? && user.fries? handle_things() end
Figuring out what’s going on here is pretty much impossible without digging into
In case you never heard about the DRY principle, or why its a pretty good idea Wikipedia is a good place to start reading about it.
Let’s say there are a few places where a ‘create blog post’ button should appear depending on whether you are allowed to create new posts or not. Chances are you will have to ask the same question more than once. Chances are the requirements to write a blog post will change. Tomorrow, management decides that it makes no sense for admins to be able to create blog posts. Now you will have to search for all
if statements that guard the display of the buttons and change them one by one. Sure you can automate this step but there is still a chance you will miss one. And in any case it’s unnecessary work.
Imagine we had we asked the right question, though:
class User def can_write_posts? self.author? || self.admin? end end # templates if user.can_write_posts? # show create blog post button end
This makes it trivial to just change the
can_write_posts? method of
User and be done.
Hard to Test
Keeping stuff simple helps a lot when trying to reason about it. Testing helps a good bit, too. So, naturally tests should be held to the same standards as the code it tests: Keep it simple.
Unit tests are arguably the simplest kind of test. They usually require little setup, involve minimal state and are focused on one thing only. And as such they are the quickest to write and easiest to reason about.
To write a unit test you need a single method. You don’t have a single method, though. You have two:
admin?. So you have to test the combination of both methods, which is by definition an integration test. Since you probably don’t want to write a test to see if combining two boolean values (the return values of
admin?) works correctly, you have to write a test for the whole view. And view tests are usually complicated and a far shot from the simplicity of a unit test.
Good Questions Are Hard to Ask
Coming up with the right question can be tough. Most of the time it only takes a few minutes, and the gain in readability and code quality will pay off for the rest of the application’s life cycle. And that means it will make the daily work life of all those who have to work with the code more pleasant. Its not unlikely that you will be among them.