There are only two hard things in Computer Science: cache invalidation and naming things. — Phil Karlton
Naming things can indeed be difficult, but the pay off for naming things properly is well worth the effort. Have you ever seen code that looks like this?
const convertObj = (x, y, z) => {const k = Object.keys(x);return k.map((key) => {return {[y]: key,[z]: x[key],}});}
What on earth does that do? You could figure it out by reading it line by line, but life would be easier if the variables had been named properly.
Good variable names are crucial, especially in dynamically typed languages, where you don’t have types defined to help you make sense of the variables. However, even statically typed languages will have the benefit of better readability when good naming conventions are used.
I’m going to share some general rules about naming I have established over the years. I’m going to break down some examples by type. Let’s start with arrays.
Arrays are an iterable list of items, usually of the same type. Since they will hold multiple values, pluralizing the variable name makes sense.
// badconst fruit = ['apple', 'banana', 'cucumber'];
// okayconst fruitArr = ['apple', 'banana', 'cucumber'];
// goodconst fruits = ['apple', 'banana', 'cucumber'];
// great - "names" implies stringsconst fruitNames = ['apple', 'banana', 'cucumber'];
// greatconst fruits = [{name: 'apple',genus: 'malus'}, {name: 'banana',genus: 'musa'}, {name: 'cucumber',genus: 'cucumis'}];
Booleans can hold only 2 values, true
or false
. Given this, using prefixes like “is”, “has”, and “can” will help the reader infer the type of the variable.
// badconst open = true;const write = true;const fruit = true;
// goodconst isOpen = true;const canWrite = true;const hasFruit = true;
What about predicate functions (functions that return booleans)? It can become tricky to name the value, after naming the function.
const user = {fruits: ['apple']}
const hasFruit = (user, fruitName) => (user.fruits.includes(fruitName));
// what do we name this boolean?const x = hasFruit(user, 'apple');
We can’t name the boolean hasProjectPermission
, as we’ve already given that name to the function. In this case, I like to prefix my predicates with either check
or get
.
const checkHasFruit = (user, fruitName) => (user.fruits.includes(fruitName));
const hasFruit = checkHasFruit(user, 'apple');
For numbers, think about words that describe numbers. Words like maximum
, minimum
, total
will .
// badconst pugs = 3;
// goodconst minPugs = 1;const maxPugs = 5;const totalPugs = 3;
Functions should be named using a verb, and a noun. When functions perform some type of action on a resource, its name should reflect that. A good format to follow is actionResource
. For example, getUser
.
// baduserData(userId)userDataFunc(userId)totalOfItems(items)
// goodgetUser(userId);calculateTotal(items);
A common convention I’ve seen used for transforming values is prefixing function names with to
.
// I like it!toDollars('euros', 20);toUppercase('a string');
Another common naming pattern I like is when iterating over items. When receiving the argument inside the function, use the singular version of the array name.
// badconst newFruits = fruits.map(x => {return doSomething(x);});
// goodconst newFruits = fruits.map(fruit => {return doSomething(fruit);});
Take these naming conventions with a pinch of salt. How you name your variables is less important than naming them consistently. If you keep a consistent naming pattern, your codebase is going to be easier to reason about, and the next developer will be able to think less.
If you have some naming conventions of your own, I would love to hear about them in the comments. If you’ve enjoyed the article, feel free to give it a clap or two.