Skip to main content

How to name your variables

· 6 min read
Conor Strejcek

Nametags

As software engineers, we generally spend more time reading code than writing it – so we should make it as easy to read as possible.

Why does a variable name matter?

Take a look at this code example:

enum MaterialType {
WOOD = "wood",
COMPOSITE = "composite",
}

interface Material {
type: MaterialType;
mult: number;
}

interface Data {
l: number;
w: number;
mat1: Material;
mat2: Material;
}

const ppsi = 50;
const ppl = 70;

function processData(data: Data): number {
const { l, w, mat1, mat2 } = data;
const p1 = l * w * ppsi * mat1.mult;
const p2 = (2 * l + 2 * w) * ppl * mat2.mult;
return p1 + p2;
}

From the example, we can see that the processData function takes in some Data, and outputs a number. We can also see that there is a type called Material, and that it has something to do with the output of our function. On the surface, this doesn't look too bad – we have types defined for our parameters, the code doesn't have confusing branching logic, and each line by itself is fairly straightforward. So what happens when someone needs to come in and make a change?

For example, what if you were asked to do the following:

Apply a 15% discount to the price of wooden railings if a customer purchases more than 400 square feet of decking.

Take a look at the same code below, with a few adjustments:

enum MaterialType {
WOOD = "wood",
COMPOSITE = "composite",
}

interface Material {
type: MaterialType;
priceMultiplier: number;
}

interface DeckSpecification {
length: number; // Inches
width: number; // Inches
deckMaterial: Material;
railingMaterial: Material;
}

const DECK_PRICE_PER_SQ_INCH = 50; // Dollars
const RAILING_PRICE_PER_INCH = 70; // Dollars

function calculateDeckPrice(deckSpec: DeckSpecification): number {
const { length, width, deckMaterial, railingMaterial } = deckSpec;
const deckingPrice =
length * width * DECK_PRICE_PER_SQ_INCH * deckMaterial.priceMultiplier;
const railingPrice =
(2 * length + 2 * width) *
RAILING_PRICE_PER_INCH *
railingMaterial.priceMultiplier;
return deckingPrice + railingPrice;
}

The only difference between the two code blocks are the names of the variables and types (and the addition of a few comments). At first glance, the code is much more verbose – and if you aren't used to naming your variables in this way, you might initially recoil from all of the extra information. However, when asked to update the code now, you should be able to make the change easily:

function calculateDeckPrice(deckSpec: DeckSpecification): number {
const { length, width, deckMaterial, railingMaterial } = deckSpec;

const deckArea = length * width;
const deckingPrice =
deckArea * DECK_PRICE_PER_SQ_INCH * deckMaterial.priceMultiplier;

// Calculate the railing discount
const railingDiscountMultiplier =
deckArea > 400 && deckMaterial.type === Material.WOOD ? 0.85 : 1;
const railingPrice =
(2 * length + 2 * width) *
RAILING_PRICE_PER_INCH *
railingMaterial.priceMultiplier *
railingDiscountMultiplier;

return deckingPrice + railingPrice;
}

In the first example, the change was almost impossible to implement, because we had no context for what the code represented. The ask was:

Apply a 15% discount to the price of wooden railings if a customer purchases more than 400 square feet of decking.

There were no references to a "railing" or "square feet," or even a "price" in the original code. It's conceivable that someone could reason about the code for long enough to come to the correct conclusion, but at the cost of extra wasted time, and with a much higher probability of making a mistake. It is likely that the developer would need to look in several other places in the codebase to get the necessary context for what the variables represent (and if the rest of the code follows this same variable naming convention, that could take a significant amount of time and effort). By giving variables meaningful names, you not only make it possible to understand code at a glance, you can also reduce the amount of related code you need to keep in your head at a time.

So what makes a variable name "good?"

The concept of self-documenting code is not a new one, but that doesn't mean that it's a solved problem. Since naming conventions are generally opinionated, there is no straightforward answer to the question, so I will be presenting my own opinions, with the hope that the general concepts behind them are informative.

Describe the representation, not implementation

Variables and functions should be named to describe what they represent in terms of business logic, and not technical details about how they are implemented.

const bool = true;
// ✗ `bool` describes the type of the variable, not what it represents

const isSelected = true;
// ✓ Human-readable description of what the variable represents
function reduceLineItems(items) {
const total = items.reduce((acc, item) => {
return acc + item.total;
}, 0);
return total;
}
// ✗ The caller does not need to know implementation details (like using a `reduce` method

function calculateTotalPrice(items) {
const total = items.reduce((acc, item) => {
return acc + item.total;
}, 0);
return total;
}
// ✓ This name is a human-readable description of what the function will do

Verbosity and abbreviations

Keeping the length of variable names manageable is a balancing act. Too short, and developers won't get any helpful context; too long and it can start to become difficult to read. In general, I find it better to err on the side of more verbose.

const l = 10;
// ✗ This name relies on context

const length = 10;
// ✓ Use the full name instead
const billOfMaterialsTotalPrice = 100;
// ✗ This could start to get difficult to read

const bomTotalPrice = 100;
// ✓ Ok if BOM is a general term used throughout your application
const len = 10;
// ✗ This abbreviation doesn't help shorten the variable name significantly, and adds uncertainty

const length = 10;
// ✓ Use the full name instead

Implied types

While Typescript gives you an explicit way to define a type for a variable, some naming conventions can also work well for specific types.

const showDetails = true;
// ✗ This variable could easily represent a function which shows the details when called

const isDetailVisible = true;
// ✓ Prefixing a boolean with `is` can help indicate its type
const sentenceArray = [];
// ✗ Suffixes like `Array` don't add much value to the types already provided by Typescript

const sentences = [];
// ✓ Use a plural name to indicate an array/collection

Next steps

Hopefully the examples in this article give you a good starting point to begin improving your own naming conventions. While there are no objective rules for good naming, the general principles of readability and business-logic context should always apply.