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
, andendl
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 ofdata
, 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
andcurr
as abbreviations foraccumulator
andcurrent
, 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
andsum
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.