If you've been coding for any amount of time you've probably come across the concept of pseudocode: a quick "mockup" of a class, object or algorithm that uses human-friendly shortcuts to get across the concept of something you're working on.
Here's some pseudocode for deciding whether or not to display a button (and which button) to a user based on their login status and permission:
if logged_in? if user_can_create_post? show_create_post_button else show_login_button
It's very clear what's happening at each step: you take it for granted that "logged_in?" will return
true if the user is logged in and
false if they aren't. We don't care about the details of what being "logged in" means at this point because it doesn't matter in the big picture of what this code is trying to do.
There's a reason we use pseudocode when trying to get a concept across: it's a very low-overhead way to demonstrate to someone, even someone non-technical, how some code will work.
One possible implementation of this in real code (in this case Ruby) could look something like:
if session[:user].present? && current_user = User.find(session[:user]) if current_user.permissions.find_by(:name => 'can_create_post').present? render :partial => 'button', :label => 'Create Post' end else render :partial => 'button', :label => 'Login' end
This works. And at the time you're writing it it's probably fairly clear to you. But come back across it 6 months from now, or show it to a new developer, and you have to stop and look at each method call and expend some mental overhead translating each step into what it actually does in the context of your app:
# does user have a user ID in session? session[:user].present? # is this a real user ID in the database? If so, save it as a local variable `current_user` current_user = User.find(session[:user]) # does user have a permission record in the database? current_user.permissions.find_by(:name => 'can_create_post').present? # show a little view snippet for an HTML <button> with the given text label render :partial => 'button', :label => 'Create Post'
I think many developers don't realize (or pretend they don't care) how much time they spend converting statements like this in their heads over and over again. If you could simplify that process, or even eliminate it completely, why wouldn't you?
That pseudocode was pretty clear...what if our real code was that simple to parse and understand? How close can we get? (Fair warning: some languages will make this an easier exercise than others. Ruby especially so. If you're coding in a strongly typed language it's going to be hard to avoid that ceremony of type declarations that will make this code much "busier".)
(Also: if you don't know, Ruby automatically returns the last statement in a method call so we don't need any explicit
return statements in these new methods.)
def show_action_button if logged_in? if can_create_post? show_create_post_button end else show_login_button end end def logged_in? current_user.present? end def current_user if session[:user].present? User.find(session[:user]) else nil end end def can_create_post? current_user.present? and current_user.permissions.find_by(:name => 'can_create_post').present? end def show_create_post_button show_button('Create Post') end def show_login_button show_button('Login') end def show_button(label) render :partial => 'button', :label => label end
That's pretty darn close. It sure is more lines of code, though. But that's a tradeoff I'm happily willing to make—I want this code to be quick to understand and edit in the future. I don't really care that a computer has to work a little harder and make the stack a little deeper in order to execute it.
We've extracted everything into methods that are a) reusable, b) clearly named for what they do and c) do one thing. And our main algorithm
show_action_button reads just like our original pseudocode.
Some additional benefits:
- DRYed up the code that rendered the button
- Simpler to test since each method can be tested independently
- If you find a problem with this code at some point in the future the sections of the code are basically labeled with what they do to make it that much easier to find
You'll come across all kinds of advice for how to break your code into reusable methods, DRYing up a section, or making sure that your methods only do one thing. If you try and write your algorithm so that it looks like pseudocode, these axioms naturally come about as a happy side effect.
Next time you're looking at a piece of code and trying to figure out if it can be made simpler, think about how you can turn that real code into pseudocode!