Any Array
should be plural (including null and array of single element when there is even slightest possibility of array having more than one elements)
// if we use variable name: number number = [0,1,2,3,4,5] for number of number // if we use variable name: numbers numbers = [0,1,2,3,4,5] for number of numbers
I’m working on—what I’ll call for now—a data migration feature. It’s for the brand new version of DoneDone, the task tracking and customer support app I’ve worked on for the past decade.
Developing a product for this long, I’m intimately familiar with its code—like a sculptor chiseling away endlessly at a large piece of stone.
The goal of this migration feature is to give our customers an easy way to bring their existing data over from the old version of DoneDone (which we call Classic) to this new version. I wish I could tell you this is a simple mapping of database tables from Classic over to the new version, but it’s far from it.
The new DoneDone is markedly different from its predecessor; Some data maps simply,other data requires some massaging,and some stuff simply can’t be mapped at all.For the next week, I chisel away at this feature from top to bottom. I develop a screen to sign in to the old system, one to let users choose the projects they want to move over, and one to see the progress of their migration requests.
On the backend, I work on a number of database updates to store these requests. I then write a separate service that picks up these requests to perform the arduous work of moving this data over “cleanly”. There are other tangential pieces I build along the way,like emailing the requester when the migration is complete and broadcasting error notifications. It’s intense work. But after a week, I feel confident about this new feature.
Before I’m ready to release it, I give my code another once over—like re-reading a manuscript from the beginning again with a fresh set of eyes. I tend to pick out things I don’t like about my code best this way.
The first thing I look at is naming. I try to use the same terms when I write code as when I talk about a feature. This avoids any unnecessary mental mapping when I transition between the screen and the rest of the world. So naturally, my codebase is littered with derivatives of the word migration
.
ClassicMigrator
project in my codebaseQueueMigrationRequest()
and MigrateClassicProjects()
EligibleForMigration
and HasMigratableProjects
Migrate
sprinkled around. The copy on the application uses the words migrate
and migration
tooOn this re-read, the word is eating at me. Mike (my business partner) and I have been using the word “migration” in reference to this feature the whole time. It’s an important word to get right.
Sometimes you use a word so much that you no longer think about what it actually means; You just know what it’s supposed to mean. Here’s the problem: We aren’t actually migrating data.
Migrate has this connotation that something is leaving one place to go to another, like a flock of birds migrating south for the winter. In our case, data isn’t leaving the Classic version. That data is still there—untouched—after the migration. I wrote it this way so existing customers can try the new system using their existing data, but if they don’t like it, they can stick with Classic.
Migrate is misleading. Using that word in the application copy might make customers apprehensive about their existing data. Using that word in code might confuse future developers about what the feature actually does.
I contemplate replacing migration with copy
. It’s clear that copying doesn’t mean removing the original. But, this isn’t quite right either. As I mentioned earlier, this data transfer isn’t a literal copy. There are some things that don’t translate perfectly, or at all. Copying also seems like a fast, mindless operation—a simple CTRL + C
CTRL + V
exercise. That’s not what this is.
I tell Mike about my conundrum.
After some thought, he suggests we use the word import
instead. Ah ha!
There’s a heftiness to the word import
that feels right. Whenever I think about importing data, I envision metallic gear icons and the momentary spiking of CPU graphs. Even when you import things in the real world, it has that same feeling of heft—huge cargo ships meandering across the ocean lugging thousands of tons of goods.
The word import
, as it’s normally used in technical terms, also doesn’t feel like data is leaving one place and going to another. I think of importing data from a file I’ve uploaded. I know the data on the file doesn’t disappear. I also don’t necessarily expect a one-to-one mapping between my data and the imported data. Import seems like the perfect word to me.
So, I end up substituting migrate
, and all its various derivatives, with import
.I’m much happier with this change.
I obsess over names. Finding that perfect name gives me the same kind of adrenaline boost I get after I’ve solved a difcult problem or figured out a much cleaner approach to an ugly solution.
My obsession with naming started from a quote I read some time ago. If you’ve written code long enough,there’s a good chance you’ve heard it as well:
There are only two hard things in ComputerScience: cache invalidation and naming things - Phil Karlton
Karlton was a software architect atNetscape. There is surprisingly little other information I could find about him though I’m sure he’s done great work. But, it’s this quote that he will be forever remembered by in the programming industry.
The first time I read this quote, I remember chuckling to myself. First,because I can think of many things that are difficult for me in programming. Second, because naming wasn’t initially among those things; I had never thought about naming as a difficult exercise. Yet, we all have written and read names that confuse, misdirect, conflate, or otherwise mystify us.
So, how do you name things well? Unlike so many other things in programming, an incoherent name won’t be caught by the compiler. There are no metrics for naming. A bad name won’t break your code. A good name won’t speed up your build.
Naming is elusive. It has a lot to do with gut, feel, style and even aesthetics. It is, in my humble opinion,the most subjective of technical subjects.
Though there are no metrics for good names, it deserves as much attention as all the other skills we preach in programming - like good architecture, writing “clean code”, or rigid testing. While these other practices are critical, they share the common drawback that you cannot see these things instantly. It’s only after digesting the codebase and working with it for a while that you reap its benefits.
On the other hand, a codebase with good names pays off immediately. They are the first things a programmer sees when reading new code. They make code more approachable. With modern tooling, you can also change the names of things safely and quickly.
This book is about how focusing on names can drive us toward better code—regardless of the languages, tools, or development environment you use.Many of the examples in this book come directly from, or were inspired by, real code I’ve written for DoneDone over the past decade.
I think the term object-oriented programming is misleading.
Textbooks tend to explain concepts like classes and interfaces by using dogs, cats, and other domesticated cuddly animals. But, these aren’t realistic examples for most coders. Most of the things we call “objects” in code don’t have a direct physical representation in the real world.
We should really call it noun-oriented programming. A noun can be a person, place,thing, or idea. Most classes really are ideas with functionality.
Classes sometimes manifest because we find ourselves with a set of functions and properties that have a common purpose that we want to wrap up in a neat little bundle. But, these classes don’t always have a physical translation. This is where wishy-washy names like UserManager
,MessagingHelper
, and AppHandler
are born.
Working through a codebase littered with these kinds of class names reminds me of working in a bloated organization where everybody is some form of middle-management. When should I direct a question to the Regional Vice President
versus the District Assistant Principal
?
When I read code like this, I have to dig into these classes to figure out what purpose they serve. When I know there’s some functionality out there I want to leverage, I have a harder time remembering where it lives. Was it in that helper doohickey
or in the other manager thingamajig
?
There are ways around this. Generic names might be a sign that the guts of the class belong elsewhere. For instance, maybe the methods inside that UserManager
class can be moved into the User
class itself. It might also be a sign that the class does too many things and needs to be split up into smaller pieces. Perhaps there are natural groupings inside that AppHandler
class-one that handles initialization, one that handles routing, one that deals with exception handling, and so forth. More specific names can be derived from there.
If it’s neither of those cases, sometimes I just have to face reality: A class can be hard to name because it does something that doesn’t easily translate in the real world. That’s when a little imagination helps. Even when a class is responsible for something that only makes sense in my code, there’s usually some metaphorical noun out there I can apply to it to make it memorable. This makes it easier to recall when I need to revisit that “object” again.
The other day, I was looking at code I wrote awhile back that allows DoneDone users to reset their password. The reset password function works like most other apps:
On this re-read, I don’t like how the token concept is implemented. Bits of logic are sprinkled in too many places. The token is encrypted on one layer of the stack and decrypted on a completely different layer. There are also a couple of places where I write repeated code to check whether the token is still valid.
But, what makes me most hungry to clean this code up is that the concept is nearly identical to the process a user takes to complete their initial registration.
It’s clear that a better approach is to wrap up this token into a single object. Let the object handle all of it—the encryption, decryption, and validation of the token. Then, I can re-use it for both password resets and user registrations.
I get to a place I’m quite happy with. The guts of the object look something like this.
public sealed class AuthToken { private readonly DateTime _utc_date_issued; public readonly int UserID; public readonly string EmailAddress; public readonly string EncryptedToken; public AuthToken(int user_id, string email) { UserID = user_id; EmailAddress = email.ToLower().Trim(); _utc_date_issued = DateTime.UtcNow; EncryptedToken = // Omitted for simplicity... } public AuthToken(string encrypted_token) { try { EncryptedToken = encrypted_token; UserID = // Deduced from the token... EmailAddress = // Deduced from the token... _utc_date_issued = // Deduced from the token... } catch { throw new InvalidInput("Cannot decrypt token!"); } } public bool IssuedWithinMinutes(int minutes) { return ( _utc_date_issued.AddMinutes(minutes) > DateTime.UtcNow ); } }
Taking a quick walkthrough of this object, you’ll notice that there are two constructors.
One hydrates the properties of the object with a user_id
and email
of the user when a password reset (or registration completion request) is initiated The timestamp of the token is set to the moment the instance is created. It also wraps all of this data together into an encrypted token.
The other hydrates the same object with the encrypted token. The token is decrypted and the other properties are deduced from the decryption. This is called during the request when a user clicks the link they received from the email.
The IssuedWithinMinutes
public method allows code elsewhere to decide whether to honor the request. For instance, a password reset link might be valid for only ten minutes whereas a user registration link could be valid for a few hours.
I’m giddy with the promises of such an object. I’m able to clean up some duplicate logic used by both the password reset and user registration processes. Whereas the encryption and decryption process once lived in random helper methods on different layers of the stack, they now have a comfortable home.
The last hurdle, however, is a big one. What do I name this thing? This “object” is not a dog or cat. I don’t really know what this thing is.
My initial attempt, AuthToken
was half-hearted. I just wanted to get something down so I could finish the implementation. Reading this name again brings up all sorts of questions and lackluster answers.
AuthToken
while also having a property with the name EncryptedToken
is confusing. It’s more than just the token. It’s easy to get into the trap of naming an object for only part of its reason for being.AuthToken
is about as generic as UserManager
.This clearly isn’t the right name. But, what comparable thing possibly exists in the real world like this?
I begin to think about something that an authority
creates for someone, who can later exchange this thing to do whatever the authority said they could do.
A ticket comes to mind. But, that conjures up thoughts of going to a sporting event or movie premiere, as if there’s a specific time and place to redeem it. It also makes me think of a parking ticket. Password resets and user registrations are neither particularly exciting nor dreadful. The metaphor doesn’t feel quite right.
A permit? A permit is valid for a set period of time and it lets someone do something agreed toby an authority until it expires. Plus, a permit is something given to you usually by a governing body, not your local movie theater or sporting venue. This feels spot on.
I end up changing the class name to PermitForUserUpdate
. The implementation instantly feels more readable. For instance, the method IssuedWithinMinutes()
reads more naturally when used in context. Permits in the real world are normally issued
. Here’s how I can validate a password reset permit hasn’t expired yet.
var permit = new PermitForUserUpdate(encrypted_token); if (!permit.IssuedWithinMinutes(10)) { throw new Exception("This permit is expired!"); }
When opportunities like these present themselves in your code, stop for a minute and see if you can find a replacement for that really vague class name. Renaming classes might require a little bit of imagination, but done repeatedly, your objects become more memorable and your codebase starts to read more fluidly.
There never seems to be time to cleanup code
No client wants to pay for it. No product manager wants to see energy spent with no feature improvements. You sometimes have to clean up surreptitiously as you go. Do it behind their backs.
“Of course, many [managers] say they are driven by quality but are more driven by schedule...In these cases I give my more controversial advice: Don’t tell! Subversive? I don’t think so.” - Martin Fowler
The good news is, you don’t need a long stretch of dedicated time to make positive impacts on your code. You can get a lot done in small spurts. One of my favorite exercises is one of the simplest: Find things to name. I look for bits of overexposed logic, then replace the logic with a method or property that I candene with a meaningful name. Done repeatedly, it can quickly make souring code sweet.
On the new DoneDone, we’re using Vue.js to deliver most of our front-end. I notice a conditional on a Vue element that looks like this:
<div v-if="![’xs’, ’sm’, ’md’].includes($mq)">...</div>
If you’re unfamiliar with Vue syntax, no big deal. The v-if
attribute works just like a normal if
statement, with a Typescript expression inside of it. Inside an HTMLelement, it determines if that element should be rendered at all. In this case, I have a <div>
that I only want to show if the statement ![’xs’, ’sm’, ’md’].includes($mq)
is true
.
But, what does this code actually mean? Well,it evaluates to true
if the “extra small”, “small”,or “medium” media query breakpoints are not hit based on the size of the browser. Put more meaningfully, it tells us if the current browser width is sized to at least the width of a normal desktop screen.
When I scan this statement, it looks cryptic. Out of place. Too specific in the context of the code around it.
I can quickly fix this line by swapping the logic with a well-named property, like isDesktopWidth
.
isDesktopWidth(): boolean { return ![’xs’, ’sm’, ’md’].includes(this.$mq) }
Now, I get the satisfaction of cleaning up my original code up with something much more approachable.
<div v-if="isDesktopWidth">...</div>
Not only does this read better, but I have a property I can reuse again in other parts of the application.
You can spot an opportunity like this from a mile away—an overly technical bit of code lying around without a proper home. I usually write code like this on the first pass, when I’m just trying to get a feature to work right and I don’t care about where all the pieces fit. But, if I never make that second pass, then things quickly turn ugly.
In my earlier days, it would be easy to forget to do that second pass because I got lost in the relief of simply getting code to work right—or I was already past the deadline I had set for myself to do so.
I don’t skip that second pass anymore. It’s this pass where I focus heavily on naming. Where I look to say what rather than how. Where the readability of my code improves dramatically. It’s as critical a step as the first.
Take a moment to look at your own code—regardless of where you are in your stack. You might be surprised how many bits of messy logic are sprinkled about that you could wrap up into a meaningful name.
Logic bits don’t always have to look overly technical to benefit from replacing it with a named method or property.
Even in a case where I might see business logic already using the well-named properties of an object, there’s usually a way I can name that piece of logic and push it back into the class definition. Here’s an example.
I have a Person
class that houses some basic information used throughout my codebase.
public class Person { public string FirstName; public string LastName; public DateTime LastAccessTimestamp; public AccountRole Role; ... }
Instances of Person
naturally spring up all over the place.
<div> <h2> @authedPerson.FirstName @authedPerson.LastName </h2> @if (authedPerson.Role == AccountRole.ADMIN || authedPerson.Role == AccountRole.OWNER) { <a href="...">Edit</a> | <a href="...">Cancel</a> } </div>
For example, on a person’s profile screen, I have an instance of Person
named authedPerson
used to display an user’s full name and a few links to other sections of the application, but only if they are an admin or owner in the account.
In another part of the application, I use a person’s first name and last initial to prep notification messages when they update a task.
var subject = person.FirstName + " " + person.LastName.Substring(0,1) + ". updated the task.";
I also have a method inside of a security class that checks if a person has accessed the application within an hour. If not, I require them to log in again.
if ((person.LastAccessTimestamp - DateTime.UtcNow).TotalMinutes > 60) { // Log out and send to the login screen. }
There are a handful of other occurrences like the examples above, where little bits of business logic against a Person’s properties are sprinkled about. Most of these bits feel so inconsequentially minor—simple, one-line constructions and statements—you might not even consider them to be “business logic” at all.
For instance, in the problem screen example,displaying a person’s first and last name might not seem like logic, but it is—it represents a person’s full name. I can push this bit of logic back to the Person
class itself and name it something meaningful.
public string FullName { get { return FirstName + " " + LastName; } }
The same can be done for the special format of the person’s name in the subject line of the notification message. I could call this an abbreviated name.
public string AbbreviatedName { get { return FirstName + " " + LastName.Substring(0,1) + "."; } }
Back on the problem screen,I can move the check for whether a person is an admin or owner as a property of the Person
instead. In this case, the check determines whether this person has administrative access.
public bool HasAdminAccess { get { return Role == AccountRoleType.ADMIN || Role == AccountRoleType.OWNER; } }
HasAdminAccess
is a sound name for this new property.
The application access logic against the Person
object within the security class can be pushed back to the class in a couple of ways. Here’s that conditional statement again.
if ((person.LastAccessTimestamp - DateTime.Now).TotalMinutes > 60)...
There are a few ways I could go about moving this. The entire statement is asking if the person has been idle for more than 60 minutes, so I could take this entire statement and turn it into a boolean
property off Person
like so: