DEV Community

Cover image for Naming: Every Developer's Nightmare
Samuel Braun
Samuel Braun

Posted on • Updated on

Naming: Every Developer's Nightmare

Aah.. naming things. A developer's favorite brain sport, nestled somewhere between attending endless meetings and refactoring code. As developers, we know that naming can be both a blessing and a curse. It's a critical part of our work, but it can lead to some hilarious but most likely frustrating names. In this post, I'll show a way we can think of variables and how you can use them effectively. Even if you feel confident naming your variables, are you also using their full potential? 🤔

My name is Jeeeeff

A Naming Pattern to Save the Day

When you find yourself struggling to name a specific variable, function, or class, consider using the following pattern and ask yourself the corresponding questions. The pattern is [scope][typePrefix][baseName][qualifier][typeSuffix].

  1. Scope:
    • What is the visibility or accessibility of this variable? (e.g., private, public, protected, internal, package-private)
    • Is this variable specific to any programming language features or conventions? (e.g., double underscore prefix in Python, dollar sign in jQuery, '@' for instance variables in Ruby)
    • Is this variable part of a framework or library that requires a specific naming pattern? (e.g., Angular's 'ng' prefix for directives, React's 'use' prefix for hooks)
  2. TypePrefix:
    • Is this variable a boolean or a function that returns a boolean? (e.g., is, has, contains, are, integrates)
    • Does the variable represent a state, condition, or action that can be described with words like "is," "has," "contains," or "integrates"? (e.g., isEnabled, hasAccess, containsItem)
    • Is this a function responsible for getting, setting, fetching, or updating data? (e.g., get, set, fetch, update, retrieve, store, save)
  3. BaseName:
    • What is the primary purpose of this variable? (e.g., user, distance, payment, errorMessage)
    • Can I describe the variable's role in the code with a clear, concise word or phrase? (e.g., registrationStatus, totalPrice, elapsedTime)
  4. Qualifier:
    • Are there any additional details or context that would help distinguish this variable from others with a similar purpose? (e.g., firstName, lastName, phoneNumber)
    • Does the variable have specific units or properties that should be included in the name, such as "InMeters" or "InSeconds"? (e.g., distanceInMiles, timeInSeconds)
    • Is there a specific state or condition that this variable represents, such as "Valid" or "Removable"? (e.g., isValidEmail, isRemovableItem)
  5. TypeSuffix:
    • What is the fundamental purpose or structure of this variable? (e.g., Count, Index, Sum, Average, List, Map, Array, Set, Queue)
    • Can the variable's role be clarified by adding a suffix for the structure like "Count," "Index," "Sum," "Average," "List," or "Map"? (e.g., itemCount, currentIndex, totalPriceSum, ratingAverage, userList, settingsMap)

Here are 6 examples:

  1. __isUserLoggedIn:
    • scope: __ (Python private variable)
    • typePrefix: is (boolean)
    • baseName: User
    • qualifier: LoggedIn
  2. fetchProductById:
    • scope: “” (Public methods dont have a specific prefix in most languages)
    • typePrefix: fetch (function)
    • baseName: Product
    • qualifier: ById
  3. updateEmailPreference:
    • scope: “” (public)
    • typePrefix: update (function)
    • baseName: Email
    • qualifier: Preference
  4. $distanceInMetersInput:
    • scope: $ (jQuery variable)
    • typePrefix: “”
    • baseName: distance
    • qualifier: InMeters
    • typeSuffix: Input
  5. getUserByEmail:
    • typePrefix: get (function)
    • baseName: User
    • qualifier: ByEmail

There may be times where you need to swap the base name and qualifier in order to give the variable a more appropriate and meaningful name. Example: getLoggedInUser instead of getUserLoggedIn.

  1. getLoggedInUser
    • typePrefix: get (function)
    • qualifier: LoggedIn
    • baseName: User

Naming Variables Effectively

Alright, so you've got a solid plan for naming variables 😎. But that's only half the battle. The other half? Comments. But not in the way you're thinking.

You might have heard the advice to scatter comments throughout your code to make it more understandable, especially for your future self. But, in reality, that's not always the best approach.

Fail

Consider this: if your code is incomprehensible without comments, then the problem isn't a lack of comments. And if your code is already clear, then you don't need the comments in the first place.

So, what's the point I'm trying to make? Use variables as comments.

When you store everything in a variable with a meaningful name, you can often understand the entire code just by reading those names and key control structures like if statements. Let me show you an example:

The other day, I came across a piece of code that took me about 10 minutes to grasp completely 🥴.

if (!row.doc[otherField]) {

    let val;

    if(currentField == "left") {
        val = row.doc[currentField].charAt(0) === "-" ? row.doc[currentField].slice(1) : row.doc[currentField];
    }

    if(currentField == "right") {
        val = row.doc[currentField].charAt(0) === "-" ? row.doc[currentField] : `-${row.doc[currentField]}`;
    }

    row.doc[otherField] = val === "-0" ? "0" : val;
    row.refreshField(otherField);
}

if (currentField === "left" && row.doc["left"] && row.doc["left"].charAt(0) !== "-") {
    row.doc["left"] = row.doc["left"] === "0" ? row.doc["left"] : `-${row.doc["left"]}`;
    row.refreshField("left");
}

if (currentField === "right" && row.doc["right"] && row.doc["right"].charAt(0) === "-") {
    row.doc["right"] = row.doc["right"].slice(1);
    row.refreshField("right");
}
Enter fullscreen mode Exit fullscreen mode

So let's clean it up by putting stuff into variables and giving them meaningful names:

const valueOfOtherField = row.doc[otherField];
const valueOfCurrentField = row.doc[currentField];
const valueOfLeftField = row.doc["left"];
const valueOfRightField = row.doc["right"];
const isCurrentFieldOnLeft = currentField === "left";
const isCurrentFieldOnRight = currentField === "right";

const startsWithMinusSign = (str) => str.charAt(0) === "-";
const removeMinusFromZero = (str) => str === "-0" ? "0" : str;
const ensureMinusAtStart = (str) => startsWithMinusSign(str) ? str : `-${str}`;
const removeMinusFromStart = (str) => str.replace(/^-/, "");

if (!valueOfOtherField) {
    let val;
    if (isCurrentFieldOnLeft) {
        val = startsWithMinusSign(valueOfCurrentField) ? 
            removeMinusFromStart(valueOfCurrentField) : 
            valueOfCurrentField;
    } else if (isCurrentFieldOnRight) {
        val = startsWithMinusSign(valueOfCurrentField) ? 
            valueOfCurrentField : 
            ensureMinusAtStart(valueOfCurrentField);
    }

    row.doc[otherField] = removeMinusFromZero(val);
    row.refreshField(otherField);
}

if (isCurrentFieldOnLeft && valueOfLeftField && !startsWithMinusSign(valueOfLeftField)) {
    row.doc["left"] = removeMinusFromZero(ensureMinusAtStart(valueOfLeftField));
    row.refreshField("left");
}

if (isCurrentFieldOnRight && valueOfRightField && startsWithMinusSign(valueOfRightField)) {
    row.doc["right"] = removeMinusFromStart(valueOfRightField);
    row.refreshField("right");
}
Enter fullscreen mode Exit fullscreen mode

With this change, the code reads almost like plain English. However, upon closer inspection, we can identify some unnecessary logic steps that can be removed and simplified. Take this line, for example:

val = startsWithMinusSign(valueOfCurrentField) ? 
            valueOfCurrentField : 
            ensureMinusAtStart(valueOfCurrentField);
Enter fullscreen mode Exit fullscreen mode

Logically, it can be further simplified. If the value starts with a minus sign, we keep it; otherwise, we add a new minus sign. Essentially, this means we can simply add a minus sign at the beginning. So, the line can be simplified to:

val = ensureMinusAtStart(valueOfCurrentField);
Enter fullscreen mode Exit fullscreen mode

Assuming the performance of row.refreshField is negligible, the same logic can be applied to the if statements by removing the && startsWithMinusSign(valueOfRightField) and && !startsWithMinusSign(valueOfLeftField) conditions. The entire code should now look like this:

const valueOfOtherField = row.doc[otherField];
const valueOfCurrentField = row.doc[currentField];
const valueOfLeftField = row.doc["left"];
const valueOfRightField = row.doc["right"];
const isCurrentFieldOnLeft = currentField === "left";
const isCurrentFieldOnRight = currentField === "right";

const startsWithMinusSign = (str) => str.charAt(0) === "-";
const removeMinusFromZero = (str) => str === "-0" ? "0" : str;
const ensureMinusAtStart = (str) => removeMinusFromZero(
    startsWithMinusSign(str) ? str : `-${str}`
);
const removeMinusFromStart = (str) => str.replace(/^-/, "");

if (!valueOfOtherField) {

  // If the current input field is on the left, let's remove the minus from the start
  // and if its on the right, let's add the minus at the start. And put it into the
  // other field. (NOTE: In the original code there were exactly two fields, left and right.)
    const newValue = isCurrentFieldOnLeft ?
        removeMinusFromStart(valueOfCurrentField) :
        ensureMinusAtStart(valueOfCurrentField);

    row.doc[otherField] = newValue;
    row.refreshField(otherField);
}

// If the current field is the left one and there is an inputted value then
// make sure to add the minus at the start
if (isCurrentFieldOnLeft && valueOfLeftField) {
    row.doc["left"] = ensureMinusAtStart(valueOfLeftField);
    row.refreshField("left");
}

// If the current field is the right one and there is an inputted value then
// make sure to remove the minus from the start
if (isCurrentFieldOnRight && valueOfRightField) {
    row.doc["right"] = removeMinusFromStart(valueOfRightField);
    row.refreshField("right");
}
Enter fullscreen mode Exit fullscreen mode

At this point, we can easily read through the code without tearing our hair out. The code accomplishes two main tasks:

  1. It ensures that when the user enters values in the left and right fields, the right one will always be positive, and the left one will be negative.
  2. If one of the fields hasn't been filled in yet, the code copies the value of the current input field to the other field.

Understanding this behavior with the original code would have been quite challenging. However, by using variables more effectively, we can abstract away the complicated syntax mess and make the code read more naturally 💪.

Let me know what you think in the comments. Maybe you have some more suggestions. If you found this post helpful, consider liking it and following for more posts in the future. Your support means a lot and encourages me to keep sharing more ❤. Happy coding!

My name is Thooor

Top comments (23)

Collapse
 
sollyucko profile image
Solomon Ucko

I think ensureMinusAtStart would be a clearer name for the conditional version of addMinusAtStart.

Collapse
 
samuel-braun profile image
Samuel Braun

You're right! I wasn't sure if "ensure" was clear enough but hearing it from an independent source.. imma change it 👍

Collapse
 
jmccabe profile image
John McCabe

ensureMinusAtStart() is semantically correct if you are doing that, i.e. if you are adding a minus only if there is not one already there. In the case of:

val = startsWithMinusSign(valueOfCurrentField) ? 
            valueOfCurrentField : 
            ensureMinusAtStart(valueOfCurrentField);
Enter fullscreen mode Exit fullscreen mode

I would be inclined to retain an addMinusAtStart() function such that you have something like this (C++ kind of thing) :

std::string addMinusAtStart(std::string input) 
{
    const std::string minus { "-" } ;
    return minus + inputn
} 

std::string ensureMinusAtStart(std::string input) 
{
    const char minus { '-' };
    return ((input[0] == minus) ? input : addMinusAtStart(input));
} 
Enter fullscreen mode Exit fullscreen mode

Just a thought though.

Collapse
 
masterxilo profile image
Paul Frischknecht

having learned more about the importance of idempotency (aka desired state pattern) in (distributed) information systems, I use "ensure" all the time now. For example instead of delete(x), we might even write ensureDoesntExist(x).

Collapse
 
jmccabe profile image
John McCabe

You need to review your use of if (!valueOfOtherField) if you're targeting clarity. There's no evidence in your code that valueOfOtherField is a boolean type, but (based on C-type-derived languages) the ! operator takes a boolean as input and produces a boolean output.

It looks like you're relying on either an overload of the boolean ! operator, or an implicit cast of valueOfOtherField to a boolean type. Either case is not clear. I have no idea of the type returned by row.doc[otherField] so I would hope that a conditional that used that returned value would be a proper value comparison retuning a boolean (e.g., where the return type us a string, if (valueOfOtherField.isEmpty()).

Collapse
 
tythos profile image
Brian Kirkpatrick

Naming event listeners in particular, which can be asynchronously managed and therefore trickier to debug within context, is a great exercise. "on[Before|After][state][change]" is a good template here.

Collapse
 
samuel-braun profile image
Samuel Braun

Great, haven't thought of that one 👍 Thanks for sharing

Collapse
 
tythos profile image
Brian Kirkpatrick

Good references/examples in the careful thought put into the VS Code extensions API:

vscode-docs.readthedocs.io/en/stab...

Collapse
 
pcjmfranken profile image
Peter Franken

German "Gründlichkeit" at its finest. Very good!

Using logical scheme-based, objective descriptions saves lots of time and effort all around. This is especially helpful in large or long-lived projects, but, honestly, once you get used to naming things like this, you wouldn't have it any other way even in small one-off personal projects, either.

Just Do It meme

Collapse
 
brense profile image
Rense Bakker

Love the intro, more true words were never spoken xD

Collapse
 
adarshgoyal profile image
Adarsh Goyal

how to name a array which will hold two different data let say town data and city data based on condition.

I have named it cityTownData is there any better way to do that

Collapse
 
samuel-braun profile image
Samuel Braun • Edited

I did something similar a few weeks ago and needed to change it back. If you can, make them two seperate arrays as this becomes easier to scale and work with in the future. But if not, you could name it cityAndTownData to avoid confusion that "city" is just a descriptor for "town" and "cityTown" is one thing. The condition you can add at the end like this: cityAndTownDataWithLargePopulation or even better cityAndTownArrayWithLargePopulation or citiesAndTownsWithLargePopulation. "data" is too general and could be anything, so replacing it with "array" you give developers a better idea of the type. So final answer would be: citiesAndTownsWithLargePopulation

Collapse
 
adarshgoyal profile image
Adarsh Goyal

as it will be either city or town data so can i make it cityOrTownArray or should i keep them seprate ?

Thread Thread
 
samuel-braun profile image
Samuel Braun • Edited

Don't overengineer your variables and spend too much time thinking about one. The core purpose of good naming is so your code can be easily read by others and yourself without feeling stupid. If you feel stupid reading other peoples code its most likely their fault. Just check your entire function and ask yourself the question. Is the code as a whole clear and understandable? If yes youre fine.

PS: If cities and towns are two different things in your code, seperate them. If they are pretty much the same, just use one word, or a generalization word like "settlement".

Thread Thread
 
adarshgoyal profile image
Adarsh Goyal

ठीक है

Collapse
 
erinposting profile image
Erin Bensinger
Collapse
 
grimmdb profile image
GrimmDB

Great article of a fundamental subject.

Collapse
 
rcoswe profile image
rcoswe

This certainly makes sense to me. Naming is hard and having this pattern as a guide is really useful.

Collapse
 
webdynamics profile image
Vlado Petrushevski

Excellent points raised!

Collapse
 
tinaheiligers profile image
Christiane (Tina) Heiligers

Your tips are fantastic, thank you!

Collapse
 
fruntend profile image
fruntend

Сongratulations 🥳! Your article hit the top posts for the week - dev.to/fruntend/top-10-posts-for-f...
Keep it up 👍

Collapse
 
vadorequest profile image
Vadorequest

ChatGPT is my new best friend for naming things, no human can compare with all the (in)sanities it provides me!

Collapse
 
crusoexia profile image
Crusoe Xia

Why is naming hard? Because it is the abstraction itself.