giant robots smashing into other giant robots

Written by thoughtbot

lolconomy

Name It

There will be Ruby in the middle of this blog post. But first, a tangent in which we explore the lambda calculus. Don’t worry too much about being lost in this next section; we’ll fix that immediately afterward.

A Simple Language

There is a programming language named the lambda calculus. It’s rather minimal: an expression, E, is one of three things:

  • a variable, V, like a, b, or c
  • a function of one variable, written λV.E
  • two expressions applied to each other, written E E

For example, the identity function is written:

λx.x

And a function that takes two arguments and produces the first is written:

λx.λy.x

You might want to try it out interactively.

We can encode numbers using this programming language, with some creativity. For example, this is the number zero:

λf.λx.x

And this is one:

λf.λx.f x

And this is two:

λf.λx.f f x

And so on. We can even add them, using this addition expression:

λm.λn.λf.λx.m f (n f x)

(This is far too tangental, but this definition of numbers and addition is an early example of object-oriented programming.)

For example, to add the number one and the number two, we’d write this:

(λm.λn.λf.λx.m f (n f x)) (λf.λx.f x) (λf.λx.f f x)

Name It

There is an oft-included “extension” to the lambda calculus that might help: naming. Let’s add this expression to our language:

  • a name definition, written VALUE ≡ E

So now we can write:

ONE ≡ λf.λx.f x
TWO ≡ λf.λx.f f x
ADD ≡ λm.λn.λf.λx.m f (n f x)

THREE ≡ ADD ONE TWO

Law Of Demeter

Here is another case, in another programming language, where naming is useful:

User.where('admin').each do |user|
  user.articles.where('published_at IS NULL').each do |article|
    article.update_attributes(published_at: Time.now)
  end
end

Here it is after introducing some names:

User.admins.each(&:publish_articles)

And here it is after a further simplification:

User.publish_admin_articles

Take It Further

In the first naming section, we named concrete nouns; in the second, we named verbs. Let us now name a concept:

class SignUp
  def initialize(params)
    @account_params = params[:account]
    @user_params = params[:user]
    @credit_card_params = params[:credit_card]
  end

  def run
    account = Account.new(@account_params)
    if account.save
      user = User.new(@user_params.merge(account: account))
      if user.save
        credit_card = CreditCard.new(@credit_card_params.merge(user: user))
        if credit_card.charge
          user.charged
        end
      end
    end
  end
end

Above we have named the concept of signing up, and given it behavior. We can name even more concepts: merging two users, generating flash messages, processing image files, building a model of a couch.

Taking It Too Far

Mulva? Gipple? Dolores!

The average human vocabulary is around 10000 words. While the jury is still out on what the maximum number is, we can all agree that keeping the number of names low is useful for keeping all of an app in your head at once.

There is another trick for more easily understanding an app: a common vocabulary. When you see a User model, you know what it means; when you see an Enrichment class, that’s puzzling. By naming more things uniquely, you have reduced the vocabulary overhead.

Or is this a red herring? Another way to keep the vocabulary low is to name everything, but have very few things to name. Push stuff into frameworks, libraries, and APIs when possible, and out of scope otherwise. You ain’t gonna need that Bracelet class.

Not Taking It Far Enough

The idea of a common vocabulary is enticing. User, article, comment, controller, singleton, and enumeration are all names we Rails developers understand. Monad, cut, disjunction, pointer—those are other people’s vocabularies. But what if …

What if we outgrew our vocabulary and started poaching theirs? And not like how we poached “functional test” and “closure”—I mean actually use their words the way they are defined.

What if instead of this …

def map(&f = lambda {|x| x})
  accumulation = []
  each do |element|
    accumulation << f.call(element)
  end
  accumulation
end

… we wrote this:

def map(&f = id)
  inject([]) do |accumulation, element|
    accumulation + [f.call(element)]
  end
end

Heck, what if we went further and wrote this:

def map(&f = id)
  inject(empty) do |accumulation, element|
    accumulation + wrap(f.call(element))
  end
end

This uses a method Kernel#id to name the common lambda {|x| x} abstraction; a method empty that produces the empty version of whatever object this Enumerable is mixed into; and a similarly-defined method, wrap, that projects the given object into the Enumerable. Now it produces arrays, linked lists, sets, tries, maybes, and so on, as needed.

And now we can use the names identity function, functor, and monoid, too.

Readability and Conceptualization

Just as our lambda calculus example was greatly simplified by naming our functions, our way-too-long methods can be improved by naming our methods. As with anything, though, there is a trade-off that you must carefully consider.

Here’s an example of some code with very few names:

def acquire_access_token_for(c)
  res = Net::HTTP.start('github.com', 443, use_ssl: true) do |http|
    r = Net::HTTP::Post.new('/login/oauth/access_token')
    r.set_form_data('client_id' => '1234123',
                    'client_secret' => 'basdu9as',
                    'code' => c)
    http.request(req)
  end

  case res
  when Net::HTTPsuccess
    b = res.body
    if b['access_token'].any?
      b['access_token'].first
    else
      raise b['error'].first
    end
  else
    raise res.inspect
  end
end

Here’s the same example, with more names:

def acquire_access_token_for(code)
  access_token_response = access_token_post(code)

  case access_token_response
  when Net::HTTPsuccess
    extract_access_token_from(access_token_response)
  else
    handle_access_token_failure(access_token_response.message)
  end
end

private

GITHUB_CLIENT_ID = '1234123'
GITHUB_SECRET = 'basdu9as'

def access_token_post(code)
  Net::HTTP.start(*http_connection) do |http|
    request = Net::HTTP::Post.new(github_access_token_path)
    request.set_form_data('client_id' => GITHUB_CLIENT_ID,
                          'client_secret' => GITHUB_SECRET,
                          'code' => code)
    http.request(request)
  end
end

def http_connection
  [
    github_access_token_uri.host,
    github_access_token_uri.port,
    use_ssl: true
  ]
end

def github_access_token_path
  github_access_token_uri.path
end

def github_access_token_uri
  URI.parse(github_access_token_url)
end

def github_access_token_url
  'https://github.com/login/oauth/access_token'
end

def extract_access_token_from(successful_http_response)
  body = CGI.parse(successful_http_response.body)
  if body['access_token'].any?
    body['access_token']
  else
    handle_access_token_failure(body['error'].first)
  end
end

def handle_access_token_failure(error_message)
  raise error_message
end

As you can see, there’s a trade-off: on the one hand, you can now read it with ease, debug more easily, and have a vocabulary with which to discuss it with others. On the other hand, it takes more vertical space.

Naming As a Building Block

Naming is the most powerful abstraction possible. By naming something, you give other people the ability to build atop it. By re-using a name you build a common vocabulary, encouraging more people to build. Naming turns a blob of code into a sequence of patterns. Giving a name to something develops it into a concept with analogies.

So, name it.

codeulate

Beware Homonyms

When you name a class, choose a name that’s unlikely to refer more than one thing.

For example, Visitor is a bad name for a class that represents unregistered users.

The issue is that Visitor is the name of a well-known pattern. Like it or not, the Gang of Four claimed this one (that’s why they’re called a gang).

If you use this class name in a web app, I can guess which concept you’re referring to, but the ambiguity is unsettling. I’m not sure. The name has homonymatic complexity: it sounds too much like another thing.

UnregisteredUser is a better name. It’s easy to guess what this refers to.

However, notice the ambiguity is affected by the problem domain: in a voter registration app, this name would again be ambiguous.

In general, choose names so colleagues can easily guess an object’s identity.

jankowski

The Emerging Standards Bureau

So I’ve been trying to enforce some halfway-arbitrary-but-plausibly-correct standards in my own code lately. Specifically, I’ve been looking at a certain class of helper method naming patterns we’ve been using for a while, and thinking about how to be most consistent across some different scenarios.

I’m pretty sure that the pattern leading up to this decision is actually pretty well known and well followed, but I’ll pretend I don’t know that, and walk through the evolution of how I got to where I am, so you’ll understand what’s keeping me up at night.

Let’s say you have an application with posts. They’re probably written by users. I bet they’re all about interesting things, too. But, you’ve got to get them!

First you do something like this…


class PostsController < ApplicationController

  def show
    @post = Post.find params[:id]
  end

end

Ok, that’s fine. This uses a normal rails route, will raise (and be caught for a 404) if it doesn’t exist, etc. Let’s say v1 of this app launches to great acclaim, as the entire internet embraces the interesting stuff you say in your posts.

Ok, requirements change – posts#show needs to be scoped to the user who is logged in. Not only that, but lots of other stuff in v2 of the app is going to need to know about the user who is logged in, and the design team wants some way to refer to this user in the views.

So you do this in the application controller…


class ApplicationController < ActionController::Base
  
  before_filter :load_user
  
  protected
  
  def load_user
    @user = User.find_by_id session[:user_id]
  end

end

Ok, great. Now the entire application can refer to @user and know what it means. Also, if it’s nil, designers can do something special to not refer to the user.

Now you can do this in your posts_controller, which will scope the Post that gets loaded on #show to only load if it’s owned by the logged in user.


class PostsController < ApplicationController

  def show
    @post = @user.posts.find params[:id]
  end

end

Great, the requirements are met. Version 2 launches to even more critical acclaim, as all site users applaud your efforts to only show them their own posts.

Now the business team gets together and they want site users to be able to view other users and then be frustrated when they can’t view their posts. So you set about implementing the users_controller and a new set of views for it. But wait! What about the users_controller’s #show action!?


class UsersController < ApplicationController

  def show
    @user = User.find params[:id]
  end

end

Thats not gonna fly. We’re already setting @user up at the global level. Maybe that was a bad idea. Yup, that was a bad idea. We can’t hold our actions responsible for things they didn’t do, can we?

Let’s refactor that into a method instead of a before_filter in the application controller…


class ApplicationController < ActionController::Base
  
  protected
  
  helper_method :current_user
  def current_user
    @_current_user ||= User.find_by_id session[:user_id]
  end

end

…and now change your posts controller as well…


class PostsController < ApplicationController

  def show
    @post = current_user.posts.find params[:id]
  end

end

Thats much better. We satisfy all the requirements, and now the users controller can use @user until the cows come home. Version 3 is wildly popular, since the egomaniacal user base that’s been reading their own posts for so long turns out to really love the opportunity to see other users whose posts they cannot read!

Well, some time goes by and we introduce commenting. The business team, geniuses that they are, have realized that users who love reading their own posts are really gonna go nuts about commenting to themselves! So we’re Modern Web Designers, and we want to build an application with RESTful urls. For example, the “collection of all comments on a post” page might be something like…

/posts/:post_id/comments

So in the comments_controller we’re going to do…


class CommentsController < ApplicationController

  def index
    @post = Post.find params[:post_id]
    @comments = @post.comments.find :all
  end

end

Well, we had a tight deadline, but we got the feature done. The users of version 4 are happy with their new commenting abilities. But no one likes that code. Why do I need that @post variable? We’re not even using it in the views, it’s there for nothing. What could have been a one line action is a two line action, and that’s one line too many, hence I’m going to be kept up at night until we sort this out in the 4.x series.

While we’re looking at removing that extra line, we realize that using params[:post_id] all over the place is getting a bit tedious as well. Why dont we build another helper method?


class ApplicationController < ActionController::Base
  
  protected
  
  helper_method :current_post
  def current_post
    @_current_post ||= Post.find_by_id params[:post_id]
  end

end

Ok, that works. What does that do to our controller?


class CommentsController < ApplicationController

  def index
    @comments = current_post.comments.find :all
  end

end

Ah, that’s better, isn’t it? We’re back to one line, and our designers can use #current_post in the same way they’ve become accustomed to using #current_user throughout the views. Good job.

Now, in the midst of our refactoring release we find out that the next version is going to use the “account as subdomain” pattern (where http://.someapp.host scopes which account is in use). To solve that problem, we’re going to embrace this pattern again and do something like…


class ApplicationController < ActionController::Base

  protected

  helper_method :current_account
  def current_account
    @_current_account ||= Account.find_by_keyword request.subdomains.first
  end

end

Man, isn’t this great? Our designers have access to #current_user to get the logged in user from the session, #current_post to get the requested post from the params/routes, and #current_account to inspect the subdomain from the hostname and get an account. They’re just giddy, and we can sit back and look at some pretty well organized code.

Our account as subdomain feature release goes really well. The sort of person that loves to comment on their own posts which no one else can see, REALLY likes to get a username.service.host hostname to do this all on!

So now the business team wants to add a bunch of features and we run into a bit of a naming crunch.

  • When they ask to list all posts by a user under an account, we dont know if #current_account means “the account at this subdomain” or “the account from params[:account_id]”
  • When they ask to show the accounts held by a user (yes, a user can have multiple vanity subdomains), we don’t know if #current_user is from the session or the params
  • …etc.

Clearly a big mess, clearly a lot of sleep to lose, clearly some refactoring to be done.

Now, to step back. Remember at the beginning when I said there were some patterns I took for granted. That’s everything up to here. Now is where I introduce some draconian naming policies and insist they always be followed.

This pattern we have is great. We avoid creating more instance variables then we need to, and (in my opinion), the readability of all the #current_* methods is better than having either instance vars or params references everywhere. But now we’re using the same #current_* method naming for three different things…

Get something from the session ie, #current_user based on session[:user_id]
Get something from the routes/path ie #current_post from params[:post_id]
Get something from the hostname ie, #current_account from request.subdomains.first

Having gone through all of that, I’m on a quest for some new naming.

My current thinking is to use ‘session_’, ‘current_’ and ‘request_’ as the method prefixes, to refer to getting something from the session, params and request/host information, respectively. Examples…

#session_user User record based on session[:user_id]
#current_post Post record based on params[:post_id]
#request_site Site record based on request.hostname

Does anyone else use this pattern? Is there a better naming convention to use? Was my story entirely too long to explain a relatively simple question? Inquiring minds want to know.