HomeBlog
todo

When Can I Use Abbreviated Variable Names?

Back when I was in college working on my computer science degree, I also worked part-time as a teaching assistant at Santa Rosa Junior College and a few other schools, where I spent a lot of time running labs, debugging C++ and Python with students, and grading a few hundred assignments every week. All that to say, I read a lot of code! I had a number of guidelines (not written by me) for grading assignments, but one of the most controversial ones was variable naming conventions.

Most of the classes I worked on were introductory programming classes, so the students were given pretty strict code quality guidelines. The variable naming conventions were one of the most important ones, and the students were told to use descriptive variable names always, and never abbreviate anything except for loop counters.

If you've ever worked on a codebase with a lot of abbreviations, you know how hard it can be to read and understand. Abbreviations are often ambiguous, and it's hard to understand what they mean, especially if you didn't write them, or they're non-standard abbreviations. That's the point this rule was trying to make, and it's a good lesson for students to learn early on in their studies.

What about the real world?

In the real world, things aren't always so black and white. I don't want to waste a lot of time being overly prescriptive about variable names, but I also don't want to end up with a codebase full of abbreviations that are hard to understand. This slows the team down, and makes it harder to maintain the codebase. Even on personal projects it can be frustrating if I didn't follow good practices and can't read my own code later.

So, when is it okay to use abbreviations in naming? I try to avoid them, except in a few pre-defined scenarios I've come up with. The following are the criteria I use to decide whether or not to use an abbreviation, and I generally apply them when I do code reviews as well to try to keep the codebase in good shape.

Standard Abbreviations are Fine

This first one is a bit obvious, but the world of software is not without abbreviations. There are a number of abbreviations that are baked into languages and nobody would expect these to be avoided. Take this C++ example:

#include <iostream>
#include <string>

int main() {
    std::string userInput;

    std::cout << "Please enter some text: ";
    std::getline(std::cin, userInput);

    std::cout << "You entered: " << userInput << std::endl;

    return 0;
}
c

We've got lots of abbreviations here! iostream, int, std, cout, cin, and endl are all abbreviations.

You wouldn't expect these to "count" as abbreviations per se, because they are part of the language, but you'd be surprised some of the hoops I saw students jump through to avoid using them, usually to prove a point that the rule was dumb. I once had a student submit code full of preprocessor directives like #define STANDARD_INPUT_STREAM std::cin and #define END_LINE std::endl so that they could then use STANDARD_INPUT_STREAM and END_LINE in their code. I know what they were trying to prove, but it was a bit silly and made the code nearly impossible to follow. Needless to say, avoiding abbreviations can go too far.

Now that I've covered my bases with the obvious and a bit silly, let's move on to some more realistic scenarios.

Well-Accepted Abbreviations and Acronyms are Generally Fine

There are a number of abbreviations and acronyms that are well-accepted in the world. These are usually okay to abbreviate in code as well. For example, abbreviations like min, max, approx, etc. My goal here is to avoid ambiguity and confusion, so if the abbreviation is well-known and unambiguous, I'm generally fine with it.

function findMin(numbers) {
  let min = numbers[0];
  for (let i = 1; i < numbers.length; i++) {
    if (numbers[i] < min) {
      min = numbers[i];
    }
  }
  return min;
}
javascript

There's not a lot of ambiguity here regarding what min means. Maybe if we were also talking about time it could be confused for "minutes," but in this context there's not much room for confusion. It all depends on context, and even well-defined abbreviations can be ambiguous at times.

Convention Should be Followed

Many languages have well-established conventions for how to name things. For example, loops often use i, j, and k as loop counters. In JavaScript, el is often used for element and idx for index. These typically serve to simplify code and make it easier to read.

for (let i = 1; i <= 3; i++) {
  for (let j = 1; j <= 3; j++) {
    console.log(`i: ${i}, j: ${j}`);
  }
}
javascript

This is a pretty common pattern, and it's pretty easy to understand what's going on here.

Scope Size Affects Naming

This is the primary rule that I pay attention to, because it can apply in almost all scenarios and acts as a good general guideline.

For a long time, I've followed the rule of thumb that the longer a variable is in scope, the more descriptive it should be. I'm not sure where I first heard this suggestion, but it always stuck with me. If I'm writing a small mapping function, I'll often use a single-letter variable because it's only in scope for a single line, or even just a few tokens.

const data = [1, 2, 3, 4, 5];
const even = data.filter((d) => d % 2 === 0);
javascript

Normally, I'd advise against using d as a variable name abbreviation of data, but in this case, it's only in scope for a moment and it's nearly impossible to misundertand its use. You also don't have to "remember" for very long what the name means when reading code, so there's not much cognitive overhead.

Once a variable is in scope for a couple lines, I'll start to lengthen the variable names.

const data = [1, 2, 3, 4, 5];
const result = data.reduce((acc, curr) => {
  if (curr % 2 === 0) {
    acc.push(curr);
  }
  return acc;
}, []);
javascript

Here, I've used acc and curr as abbreviations for accumulator and current, respectively. These are still pretty short, but they're in scope for a few lines, so I've lengthened them a bit. This is also a fairly standard naming convention, so it's doubly acceptable in my book.

That's still a pretty short example though. Here's a longer one, where I use more descriptive names for the reduce function arguments.

interface Order {
  customerId: number;
  items: {
    productId: number;
    price: number;
  }[];
}

const orders = [...someData];

const customerRevenue = orders.reduce<{ [key: number]: number }>(
  (result, order) => {
    const { customerId, items } = order;
    const totalOrderPrice = items.reduce((sum, item) => sum + item.price, 0);
    result[customerId] = (result[customerId] || 0) + totalOrderPrice;
    return result;
  },
  {},
);
typescript

Here, we have a longer scope, and nested reduce functions. I've used result and sum as the accumulators here so that they're more descriptive, and named the current items based on what they represent.

Final Thoughts

At the end of the day, being overly prescriptive with naming conventions can be a frustrating waste of time, just like being too black-and-white about anything in software can cause issues. It's useful to have some guidelines, but there are always exceptions. I've found that the guidelines above are a good starting point, but I make exceptions when it makes sense.

If you really prefer short variable names, keeping your scopes small and composing code into smaller units will go a long ways towards making your code more readable and maintable even with abbreviated names.