A Subjective Guide To Naming Stuff In Ruby on Rails: Classes

A Subjective Guide To Naming Stuff In Ruby on Rails: Classes

There are only two hard things in Computer Science: cache invalidation and naming things. — Phil Karlton

Naming things in programming may not be hard (a matter of imagination), but it is definitely hard to do it right. Also, it turns out to be very unclear what “right” means in each and every context. It doesn’t change the fact that in 8 out of 10 PRs I review, I provide a feedback related to naming, be it naming of classes, methods, fields, variables… you name it. In many cases this feedback requires a lot of effort — you realize something is off, but it is really hard to come up with something better.

General rules for naming classes

It is not an easy task to specify one general rule that would cover all challenges related to naming classes. In an attempt to do so, the first and foremost recommendation would be to describe the responsibility of class by its name — a few examples can be found below:

  • RegistrationForm, Forms::Registration — a form object responsible for processing registration information
  • ManagersCollection — a collection (something we can iterate over) of something referred to as Manager
  • Manager — most likely a model or PORO representing some resource named “Manager”
  • RecentlyAddedManagersQuery — a query that returns a relation containing resources named “Manager”, but only “recently added” ones
  • Salesforce::SOAPQueryBuilder, Workforce::SOAPQueryBuilder — a builder (we can chain consecutive calls based on its instance) of SOAP query for Salesforce service
  • UserFactory — a factory that is responsible for creating user instances
  • Project::BulkUpdatesController — a controller dedicated for handling updates of many projects at once
  • AverageGuestRating — some class responsible for calculating or representing a scalar value of average guest rating
  • AverageGuestRatings — some class responsible for calculating or representing an array of guest ratings
  • AdminDecorator — a class responsible for decorating (modifying / extending) public interface of objects of Admin class
  • TaskSerializer — a class responsible for serializing a resource named Task
  • AirbnbClient — a class responsible for wrapping airbnb API and exposing its endpoints as methods
  • ProjectPolicy — a class responsible for defining access rules to objects of Project class
  • Purchasable — a module / extension that adds capabilities related to purchasing objects of class extended with it
  • DestroyUser, Services::User::Destroy, ImportProducts, SyncReservations, PurgeOutdatedOrders, GrantPropertyAccessToUser — service objects responsible for… destroying user, importing products etc. We could continue this list forever, yet we can already identify a few patterns:

  • In case of most names you can clearly identify the subject, be it Registration, Admin, Airbnb, Project or even the concept of BulkUpdate
  • Suffixes can clearly define the responsibility or behaviour of a given class. Those suffixes usually match a pattern name (i.e. Builder, Client, Policy, Decorator)
  • Some suffixes can help in identifying what should be passed into the constructor, especially in case of service objects (GrantPropertyAccessToUser)
  • Plural and singular form can help in identifying what a given class represents (a singular resource or value, collection etc.) or what it accepts in its constructor
  • Namespacing might be useful to clarify differences between classes that deserve the same name but in a different context (i.e. domain, service name, permissions level)
  • Stand-alone nouns usually represent values or resources / models
  • Verb-based names usually describe commands / services
  • Sometimes there are multiple ways to achieve a similar level of expressiveness (RegistrationForm, Forms::Registration)

What we should be aware of

Names with ambiguous meaning, i.e. UserVerification, CheckAccountBalance, OrderSummarizer. It might sound simple to say that given class “checks account balance”, but it is actually hard to sort out what that actually means and/or involves.

Misleading names that do not represent what the class is actually responsible for, i.e. “Dwarve” for representing “Resource” or “Goblin” representing “Page”. Even if this is hard to believe, this is not a joke. These are real names of classes from admin framework for Rails. Those names make it extremely hard to read and comprehend a code of this framework on the fly and there is absolutely no benefit from introducing them (unless you are after code obfuscation).

Do not be afraid of inventing names, or using abstract ones, but the ones that have as clear meaning as possible, at least in projects’ domain. Incinerator? Why not!

Namespacing without a plan — if we decide to use namespaces, we should introduce some conventions and then stick to them. There can be many conventions applied in one project, yet mixing them in shared contexts can be misleading. For instance, we can use service name (i.e. Trello) to group files related to integrating with this service, while in controllers we can group controllers by the access level (i.e. Admin). In such case, adding Trello namespace to controllers should be introduced with caution, as we no longer have a clear, unambiguous preference for when and how to introduce other namespaces.

Names that are too long — ActivateProjectAndSendItsOwnerNotification is a bit too expressive — if the body of the class is readable enough, then simple ActivateProject would be good enough as well. Sometimes long names are also an indicator of too many responsibilities sitting in the class itself.

Acronyms — If an acronym is well-known (e.g. SSL, SMS), then using it in a class or namespace names is not a problem at all. If it is not however, then we should be very cautious not to make our code harder to comprehend quickly. This is even more important if such acronym matches some well-known ones, but means something totally different. For example, IR representing “Image Recognition” where for many people it is a shortcut for “infrared”. It might take just a bit of effort to match it to the context of “Image Recognition” but this effort may also add up in the long term. Also in many cases, it is preferred to keep acronyms in uppercase.

Summary

Based on the above, we can come up with a few recommendations:

  1. Identify scope (and provide namespace if necessary)
  2. Identify the subject
  3. Pluralize the subject (if necessary)
  4. Identify the pattern (and provide suffix if necessary)
  5. Augment the class name with anything that will reveal this class responsibility (if necessary)

I really hope you will find this short rant about naming classes helpful next time you create a new class which deserves some good, descriptive name.

This article is sort of a living document and might be a subject of frequent updates and changes, no major ones are expected though. Written with Ruby on Rails in mind it can easily be applied to many other Object-Oriented languages and frameworks.