Episode #087: Naming Things: Headcount

Sometimes, the only thing standing between some confusing code and a better design is a better choice of names.

In this episode, you can watch a story about one of those situations. You’ll see how a pair-programming conversation led to finding a missing domain term, which clarified a problem and opened the way to further code improvements.

Upgrade to download episode video.

Episode Script

When I work with other programmers, some of the most fruitful discussions we have are the ones that center around naming things. I’ve had many pairing sessions in which we’ve struggled with some difficult design questions. Then we’ll identify and name a concept which had formerly been lying implicit in the code. Suddenly, obvious answers to our questions will seem to naturally fall out as a consequence of identifying the concept.

A lot of people have asked me how to get better at naming things. Unfortunately, there are no simple rules when it comes to naming. It’s something you learn by experience, by reading other people’s code, and through discussion with other developers.

This has left me in a bit of a bind: I’d like to cover naming in these videos; but I don’t know of any generally applicable rules when it comes to giving names to things. So instead, I’ve decided to collect examples of specific naming decisions “in the wild”, and present them along with some of the reasoning that went into them. Hopefully you’ll find some design inspiration in following the thought processes that resulted in these real-world naming choices.

Today’s example is inspired by a pairing session I did recently. Consider a program that has to do with live events. Events have attendees, who are identified by their email address.

This program also has the concept of an “attendance”. An attendance links an event to an attendee, but also contains a plus_one flag. This flag indicates whether the attendee will be bringing another person to the event.

Each event has a name, a venue capacity, and a list of attendances. It also has a couple of methods.

For various reasons we want to be able to get a count of attendees going to the event. So the class has a method called attendees which goes through the attendances and tallies them up. In order to get an accurate count, it has to take into account whether a given attendance is flagged as “plus one” or not.

We also need to be able to add a new attendance to an event. The method that handles this has some special logic. It figures out how many people the new attendance will add to the event, again using the plus_one flag on attendances. Then, if that would put the event over capacity, it rejects the new attendance. Otherwise, it adds the new attendance to the list.

Attendee = Struct.new(:email)

Attendance = Struct.new(:event, :attendee, :plus_one)

Event = Struct.new(:name, :capacity, :attendances) do
  def initialize(*)
    super
    self.attendances ||= []
  end

  def attendees
    attendances.reduce(0) {|count, attendance|
      if attendance.plus_one
        count + 2
      else
        count + 1
      end
    }
  end

  def add_attendance(attendance)
    new_attendees = if attendance.plus_one
                      2
                    else
                      1
                    end
    if attendees + new_attendees > capacity
      raise "Sorry, this event is full!"
    else
      attendances << attendance
    end
  end
end

We can identify a couple of problems with this code. For one thing, there is a method named #attendees on Event. A newcomer to the code might reasonably expect this method to return a list of Attendee objects, but in fact it returns an integer count. This is potentially surprising behavior.

Another issue is that the if/else conditional based on the plus_one flag is repeated in both the attendees and #add_attendance methods. Not only is this duplication, it looks like feature envy: a code smell wherein a class seems more interested in another object’s methods than it is in its own.

During the pairing session that inspired this code, we quickly zeroed in on the problematically-named #attendees method. We tossed some alternate terms around for what the concept this method represents. We tried terms like “attendee count” and “people”.

Finally, one of us said “head count”, and everything suddenly clicked. “Head count” is familiar, recognizable terminology for the absolute number of people at an event. In other words, it is domain language. And it is clearly distinguished from the concept of an “attendance”.

So we quickly changed #attendees to #head_count. This did away with the ambiguity in that method’s name.

Attendee = Struct.new(:email)

Attendance = Struct.new(:event, :attendee, :plus_one)

Event = Struct.new(:name, :capacity, :attendances) do
  def initialize(*)
    super
    self.attendances ||= []
  end

  def head_count
    attendances.reduce(0) {|count, attendance|
      if attendance.plus_one
        count + 2
      else
        count + 1
      end
    }
  end

  def add_attendance(attendance)
    new_attendees = if attendance.plus_one
                      2
                    else
                      1
                    end
    if head_count + new_attendees > capacity
      raise "Sorry, this event is full!"
    else
      attendances << attendance
    end
  end
end

Then, we realized that if we were talking about head counts, it would make sense for an individual attendance object to know its own number of “heads”. We created a new method called #heads on the Attendance class. This method used the same conditional logic we used in the Event class to return either 2 or 1 depending on the state of the plus_one flag.

Attendance = Struct.new(:event, :attendee, :plus_one) do
  def heads
    if plus_one
      2
    else
      1
    end
  end
end

While we were updating this class, we also decided to deal with another naming nitpick. Since plus_one was a boolean flag, we decided it would be more idiomatic Ruby for the code to use a method with a question mark on the end to access it. So we aliased the plus_one accessor to a question mark version, and updated our #heads code accordingly.

Attendance = Struct.new(:event, :attendee, :plus_one) do
  alias_method :plus_one?, :plus_one

  def heads
    if plus_one?
      2
    else
      1
    end
  end
end

Once we added the #heads method to Attendance, we were able to go back to the Event class and massively simplify it. Calculating #head_count became a simple matter of summing up the heads across all attendances. And an extra conditional could be completely removed from #add_attendance.

Event = Struct.new(:name, :capacity, :attendances) do
  def initialize(*)
    super
    self.attendances ||= []
  end

  def head_count
    attendances.reduce(0) {|count, attendance|
      count + attendance.heads
    }
  end

  def add_attendance(attendance)
    if head_count + attendance.heads > capacity
      raise "Sorry, this event is full!"
    else
      attendances << attendance
    end
  end
end

In my experience, it’s often the case that as soon as we identify an as-yet unnamed concept that’s implicit in the code, multiple beneficial refactorings seem to cascade out naturally from that change. This was certainly the case with the attendees to #head_count renaming.

I hope this little example has gotten the naming gears turning in your brain. If you find examples like these helpful let me know, and I’ll keep on documenting them as I run across them. And if you have any examples of your own—either cases where a rename clarified the code, or examples of code where you feel like the right name has yet to emerge—feel free to send them my way!

That’s it for now. Happy hacking!