TL;DR Naming in code isn't just a syntax concern, it's a design decision. Good names reduce cognitive load, but every name is a cost. In this article, we'll look at why naming is hard, why natural language can't replace code, and when it's better to duplicate than abstract.

Naming in software is famously difficult, and for a good reason. While we tend to seek to write clean and elegant code, we often forget that names themselves are a source of complexity. Names are not just labels, they are abstractions, and each one comes with a cognitive and maintenance cost. In this post we are going to explore why naming is hard, why code duplication isn't always bad and why we should seek for simplicity.

Why Programming Languages Exist?

We didn't invent programming languages because they are cool, we created them because natural languages like English are fundamentally bad at describing technical ideas. Take the Pythagorean theorem as an example, the English version is clunky, long and not easy to understand:

the sum of the squares on the legs of a right triangle is equal to the square on the hypotenuse

The algebraic notation, a² + b² = c², is far more compact and unambiguous. That's the power of a specialized language, and the same applies to programming languages.

When we write code, we are always using (at least) 2 languages: the programming language, like Python or Java, and a natural language, usually English, for naming variables, functions and files. The programming language is precise, the natural language is not. That's where the trouble begins.

Why LLMs Can't Replace Programmers

There is a growing trend to treat large language models (LLMs) as code generators. And there are some people who fear that they can replace programmers. But here is the issue: they rely on natural language to interpret intent, and that is inherently fuzzy. Prompting in a natural language to write code is like asking a poet to draft legal policy, the precision just isn't there. Programming languages exist because English can't express what we need in software clearly. They are purpose-built for clarity and logic. English isn't. LLMs are a great tool to increase productivity, accelerate learning and produce boilerplates, but they cannot replace coding.

Every Name Is a Cost

A new name in your code, where it's a variable, function or class, isn't free, it comes with cognitive load. It has to be:

  • Understood by the team
  • Maintained
  • Aligned with future code changes

Even small indirections can add up:

if (shouldSendNotification(user)) {
    send(user);
}

This way you need to check what shouldSendNotification() does. Sometimes it's better to inline simple logic:

if (user.email && !user.optedOut) {
    send(user);
}

Inline logic keeps the behavior visible at the call site, which is often more readable than a name wrapping simple conditions that adds extra indirection.

In one project, I had to debug a nested flow where every condition had its own well-named function—but I still couldn't understand the logic without jumping up and down on the code. Inlining the conditions made the bug obvious in seconds.

Names Are Bad Comments

Variable and functions names are made for humans, as the compilers don't care about them. But different than comments, you cannot use punctuations, spaces, and usually want to keep names short and identifiable. That makes it hard to find good names for many things. Like comments, names can go stale. And having a bad name can mislead the developer that will face that code in the future, making it harder to maintain and potentially generating bugs. For example, instead of this:

handleUserData() {
    // actually just filters inactive users
}

Just write:

filterInactiveUsers() {

}

And if it's simple logic? Skip the name entirely:

const filteredUsers = users.filter(u => u.lastLogin < cutoff);

Have Naming Conventions

As we are seeing, naming is hard, but having consistent naming makes it easier to read and understand, reducing the cognitive load. That's why having a clear and enforced naming convention across your codebase is critical.

Think of it this way: when you see a function named getUser() in one file, and loadCustomerData() in another, and fetchPerson() elsewhere, your brain has to stop and ask:

  • Are these the same concept?
  • Do they behave the same way?
  • Do they return the same type of object?

This mental overhead adds up especially in large teams or mature codebases. A consistent convention eliminates that ambiguity:

getUser();
getUserById();
getAllUsers();

Here, the naming scheme is predictable. You don't have to guess whether "fetch" or "load" means something different. You can scan the code faster and understand relationships between functions more easily.

Familiar Patterns Make Code Easier to Navigate

When teams follow a naming convention (e.g., verbs for functions, nouns for models, isX for booleans), it becomes easier to guess what something does—even if you've never seen that part of the code before. For example:

isActiveUser; // boolean
calculateTotal(); // function
UserOrder; // model or class

This predictability allows you to:

  • Search faster
  • Spot mistakes quickly
  • Understand new code without diving into every implementation

Naming conventions are not just about syntax, they are about shared understanding. When your whole team adheres to the same patterns, you reduce misunderstandings and speed up onboarding for new engineers. They also allow tools (linters, code formatters, type checkers) to help you more effectively. The more structured your code is, the more tooling can automate quality enforcement.

Use the Real Business Language and Terms

If you're building software for a non-English-speaking domain (Brazilian finance or Japanese logistics), don't change key business terms to awkward English translations. It's better to use the language your stakeholders use, even inside the codebase. Terms like boleto, kanban, or kakeibo might not have clean English equivalents. Translating them poorly makes the code harder to understand, not easier.

I worked at a Brazilian project in the Education field where we had individual classes, group of classes defined by the platform, and group of classes defined by the user. Internally, we used Portuguese names to describe each one of them. But in code, the teams decided to use English translations, but the backend and the frontend team didn't communicate with each other, so they ended up having different names for the same things, and even worse, we had a common name meaning two different things for the backend and the frontend. So the frontend /studyPlan page was calling /api/classes and the /classes was calling/api/courses. This misalignment wasn't just annoying, it caused real bugs and friction across the teams. Using the domain's native terms in code would have avoided the confusion entirely.

Conclusion

Writing good code isn't about abstracting everything into elegant patterns. It's about being clear, honest, and readable.

  • Don't name what doesn't need naming
  • Don't abstract every repeated line
  • Do name things based on what they are now
  • Do prefer clear duplication over vague generalization
  • Do use business terms exactly as your users say them

Fewer names means less mental overhead, fewer misunderstandings, and cleaner maintenance.

Sometimes, the best code looks boring.