Software Naming— The Test of Time

Peter Smith
CodeX
Published in
10 min readJul 12, 2021

Please read my recent blog posts at www.petersmith.net.

You’ll have a hard time finding an experienced software engineer who doesn’t have a preferred naming convention. This includes function or method names, classes, files, directories, projects, products, or potentially any concept that we talk about, or any identifier in our source code.

This blog post isn’t about typical naming conventions. We won’t discuss whether to use camelCase or snake_case, or whether an identifier should contain a noun, or a verb. We won’t specify how to pluralize, or whether suffixing with Manager or Util is a bad idea. The Internet is full of blog posts covering those topics.

Instead, we’ll focus on the long-term effects of your naming choices. The identifiers you select today may still be in use 5–10 years from now. Most developers want their software to be used long term, yet they choose identifiers that are not resilient to the passage of time. This discussion is based on more than 20 years experience of seeing these issues in the software industry, so the problems are real!

What’s the impact of non-resilient names? To be honest, there’s plenty of other technical debt you could focus on first, but learning how to select a resilient name avoids confusion in the long-term, once your product is successful and mature.

What in the World Might Change?

To illustrate the passage of time, we’ll use an example of a SaaS application. Imagine that Kitty Incorporated is a small software company with a single web-based product, KittyPics, allowing customers to upload and share their favourite cat photos.

Here are some events in the life of Kitty Incorporated:

  • Scaling Web Traffic — Initially KittyPics only required a single web server, with a single disk drive to store images. Due to the site’s growing popularity, there’s now a need for multiple web servers and multiple disks.
  • Projects Come and Go — After successfully serving cat pictures, a new internal project (code named Tiger) is started at Kitty Inc. The goal of the Tiger project is to allow upload of cat videos, rather than just photos. The project is active for six months, with all code being added to the same KittyPics code base.
  • More Flexibility— Although the original version of KittyPics did one thing (and did it well), countless new features and configuration settings were added over time. Users now have many ways to adjust their experience, requiring updated algorithms in the code.
  • Changing Technology — The CTO decides that maintaining a large number of disk drives isn’t cost effective, so a project is initiated to move all cat pictures/videos to Amazon Web Services using S3 Buckets. Similarly, a switch was made from storing meta-data in MySQL, to instead using the more scalable DynamoDB database.
  • Kitty Incorporate is Acquired — After successfully operating for two years, Kitty Inc is acquired by a competitor — Cat Pictures Corporation.
  • KittyPics is Renamed — After careful market analysis, it’s determine that KittyPics should be renamed to a much trendier name: SabreShots.

These events are common in a software product’s lifecycle. We’ll now focus on how these changes cause our identifiers to become outdated. If they’re outdated, the code becomes harder to read and more costly to maintain, therefore increasing the technical debt load.

How Names Become Confusing or Inconsistent

Let’s look at some real-world examples of how the names we choose can become confusing or inconsistent, largely due to the passage of time. For each example, we’ll see a naming choice that made sense when the software was first written, but stopped making sense at a later time. We’ll also see an example of names that are more resilient to change.

1 — Expect New Implementations, but Avoid Calling Them “New”

Let’s start with a simple example. Imagine our KittyPics software contains an algorithm for automatically positioning cat pictures on the screen. The algorithm has worked well, but we want to add a second algorithm, while still keeping the first as an optional feature.

If we started with the following code:

def layoutAlgorithm { ... }

There’s a natural tendency to define the second algorithm as:

def newLayoutAlgorithm { ... }

Clearly this makes sense at the time when transitioning from old to new algorithm, but the word new becomes less relevant over time. After a few years, developers consider both implementations as “old”, and there’s no hint about what the algorithms actually do. Also, if there was a third implementation added, would it be called muchNewerLayoutAlgorithm?

A better practice is to name the algorithms in a descriptive way to indicate how they differ. For example:

def gridLayoutAlgorithm { ... }
def circularLayoutAlgorithm { ... }

These names remain relevant as time passes, since their purpose is well-described, even years after their “new-ness” fades.

2 — Remember That Names Last Longer Than Projects

Projects and Products differ is significant ways. Projects are a time-bounded (for example, six months in duration) and have the primary purpose of adding a discrete set of features to an existing code base. In contrast, a Product has a much longer life cycle, possibly existing for 5–10 years, or even longer.

It’s important to keep in mind that any name you choose in your software will last beyond the scope of the current project. In our example of the Tiger project (to add cat videos to the KittyPics product), any use of the name Tiger becomes increasingly meaningless after the project finishes.

module Tiger
def tigerAlgorithm {
count = getTigerCount()
...
}

Newly-hired developers will struggle to wrap their minds around what Tiger means, especially as the Tiger project is now ancient history.

Instead, use names that are meaningful after the project has completed, when the newly-added features have simply become part of the long-term product code base. For example:

module VideoManagement
def videoPlacementAlgorithm {
count = getVideoCount()
...
}

This advice may seem obvious, but it’s very common to see modules, classes, algorithms, databases, or DNS names with project names embedded into them. With the passage of time, code bases are littered with temporary project names in their identifiers.

One example of this problem is using the project’s name to identify newly-created software modules. For example, Tiger may become the internal code name for the new software module that handles videos. Over time, people think of the Tiger module, rather than the Tiger project, when they hear the name Tiger, even if the original project had a much wider scope.

3 — Consider That Technology Changes, but Names Don’t Need To

Throughout a product’s lifespan, it’s common to change the underlying technology used. For example, our KittyPics product moved from using MySQL as the primary database, to using the more scalable DynamoDB. Ideally, this change should be transparent to most of the software, but that’s not always the case.

In the first implementation of KittyPics it made sense to write code such as:

mySql.getImageCount()

but that clearly doesn’t make sense when you switch to using DynamoDB. Instead, use a more generic name for your data store, such as:

metaData.getImageCount() 

You’ll still need to modify the internal code for the getImageCount() method, but if you choose your names more carefully, you won’t need to modify the code everywhere the getImageCount() method is called.

4 — Marketing Names Can Change Outside Your Control

The name of your product (or the domain objects within the product), can change due to marketing or sales decisions. In our example, KittyPics was rebranded to SabreShots, with the goal of increasing sales. Likewise, names of domain objects within the product (such as Collection or Image), may change to something new (such as Album or Photo). Experience shows that products can go through 2–3 name changes in a ten year span, largely due to corporate acquisitions or rebranding.

To solve the problem, you might choose to go through a major refactoring exercise to keep the internal code base aligned with the latest external names. This makes sense for small code bases, but for larger products it’ll only be a source of frustration and newly-introduced bugs.

Also, be very cautious about mixing the old name with the new name in the same code base (for example, passing a Photo object into a method expecting an Image object, even if they’re the same thing). Inter-mixing the old and new names can be very confusing for developers, especially when they’re developing in the code base for the first time.

Perhaps the best advice is to avoid adapting the code base at all, but instead be comfortable with internal names being different from external names. That is, always use the original names of Collection or Image, regardless of the current customer-facing name. Of course, developers need to remember the mapping from internal name to external name, but at least the code will be consistent and easier to work with.

5 — Your Company Name Can Also Change

It probably comes as no surprise, but it’s good advice to not include your company’s name in source code identifiers. Mergers and acquisitions are common, and acquired company names disappear into history.

Regardless of whether your company name changes, you should always think twice before using it in identifiers. What value does it add to have Kitty in these names? You probably want more descriptive names than these:

kittyCollectionCount = 100
def displayKittyImage() { ... }
kittyDb.executeSql(...)

Of course, DNS names are an obvious places where the company name is a requirement, so it’s not a hard rule. Interestingly, many smaller companies only sell a single product, so their company name and product name end up being synonymous, with a lot of work involved to break that association as the company grows. Was kitty.com the product, or the company?

6 — Avoid Being Too Specific About a Purpose

When implementing code, there’s a tendency to focus on solving the specific problem you have at that moment. However, as the product changes, there’s a desire to reuse code you wrote in the past. Refactoring has become a common activity for most developers, including the selection of more suitable names.

For example, imagine you had written a PhotoArranger class, providing the ability for users to manually change the order of the photos on the product home page. However, now that the home page also supports videos, the PhotoArranger name becomes confusing. One solution would be to rename the class to PhotoAndVideoArranger, but that gets even more problematic when the ability to add static text and hyperlinks is added to this class (should we now call it PhotoAndVideoAndStaticTextWithHyperlinksArranger ?)

Perhaps the original name should have been something less specific, making it more flexible and resilient to change. Perhaps HomePageArranger? However, something like MediaLayoutArranger would be even more generic, allowing it to be used on other pages (not just the home page).

7 — You’ll Likely Have More Than One

Software is often written with time-to-market as a driving factor, rather than worrying about long-term growth (which may never happen if you don’t get to market quickly). One downside of this approach is that software is designed to support only one of a particular component, rather than many.

In our KittyPics example, the following components needed to go from one instance to many instances:

  • The number of web application servers running the software.
  • The number of disks used for storing image files.
  • The number of countries in which servers are running.
  • The number of databases required for storing meta-information.

Aside from all the software design decisions you’ll need to make (such as using an array of server names, rather than a single server name), there are naming issues too.

For example, for API server names, you’d probably start with:

api.kitty.com

but when you need to support servers in multiple countries, what do you call them? The common solution is:

api.kitty.com
api-nz.kitty.com
api-uk.kitty.com
...

This example is certainly on the low-end of the technical debt scale, but it does look odd that there’s no country tag on the first entry, and it might cause confusion in your automatic deployment scripts. To avoid these scenarios, you should keep expansion in mind and add a descriptive tag on the first DNS name you use. In the case of countable things, add a -1 to the name by default, such as api-1.kitty.com.

Summary — How Much Should You Care?

In this blog post we’ve seen a number of different ways in which names fail to be resilient over time, therefore becoming stale, confusing, or inconsistent.

What should you do to fix these problems?

To be honest, although these problems occur in numerous products, in almost every company, people always manage to adjust to the inconsistencies. There’s confusion when internal names don’t match external names, especially for new employees who are wrapping their minds around a new code base. However, given enough time, people always seem to overcome the learning curve and just live with the inconsistencies.

Should we refactor our product to resolve these inconsistencies?

That decision is totally up to you, based on how much effort it’ll be to fix the problem, versus how much pain it’ll be to live with it. This is the ever-present technical debt problem we all face with legacy code. Refactoring an internal class name is a simple and pragmatic improvement, but changing a customer-facing DNS name is a challenge you might choose to ignore.

Perhaps the best advice is to try and use meaningful names when you first write the code. However, no matter how much effort you put into choosing names for the long term, you’ll never quite get it right. In the end, you must be prepared to accept and ignore the remaining problems.

--

--