Introduction
Here’s a little insight into what I think about when I write code.
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 scupltor 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 onceover—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 derivates of the word migration.
- There’s a
ClassicMigrator
project in my codebase. - Methods named
QueueMigrationRequest()
andMigrateClassicProjects()
. - Class properties like
EligibleForMigration
andHasMigratableProjects
. - There are models, views, and controllers with every derivative of Migrate sprinkled around. The copy on the application uses the words migrate and migration too.
On 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 difficult 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 Computer Science: cache invalidation and naming things.”
Karlton was a software architect at Netscape. There is suprisingly 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 mistify 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 awhile 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. Let’s begin.