DEV Community

Michiharu Ono
Michiharu Ono

Posted on

How (and How NOT) to Name Rails Models Beyond the Obvious

Have you ever encountered a model in a Rails app that initially seemed to have an understandable name, but as you became more familiar with the code, you realized the name led you to believe one thing when it actually meant something entirely different? 🙋‍♂️

This kind of naming can introduce unnecessary complexity, forcing you to "decipher" what the code actually does.

And that's never fun ☕️

Naming things in Rails is key to keeping your code easy to understand and maintain in the long run. While conventions like using snake_case for variables and methods is a no-brainer, I believe that picking the right names for models and methods requires a bit more thought.

In this blog, I’ll attempt to unpack meaningful names for your Rails models and methods, helping you avoid confusion and keeping things clear.

(Please understand that this blog isn’t a set of hard-and-fast rules 🙆‍♂️ It’s based on what I’ve found works well for me and my team in the past, especially when developers come and go and need to collaborate with multiple people. The goal is to make things clearer for everyone in the long run.)


How To Name Model

When naming Rails models, it’s crucial that the name communicates the model’s role, responsibility, and the real-world entity it represents.

In this section, I’ll cover key considerations to help ensure your model names are clear and avoid common pitfalls like vague or action-oriented names.

1. Does the Model Capture the Entity?

A well-named model should represent a real-world entity that the application works with. Typically, the name is a noun that reflects the object or concept it encapsulates, rather than an action or process. For example:

  • User represents a user entity.
  • Product represents a product entity.
  • Order represents an order entity.

On the other hand, naming a model after a process or a task (e.g., ProductProcessor) can mislead developers into thinking the model performs an action, rather than serving as a core data entity. Such models are often better suited to be service classes or similar, but that’s a topic I’ll cover in a future post on service classes 😌

2. Does It Capture the Responsibility?

In addition to representing an entity, a model's name should also reflect its core responsibility within the application. It’s not just about holding data but about encapsulating the relevant business logic for that data.

The model's name should align with its primary responsibility in order to avoid becoming a dumping ground for unrelated logic. (This happens more often than not! )

For example, a User model should primarily represent the user entity and manage core attributes like name, email, and date_of_birth.

If any additional responsibilities are needed, it is often better to delegate them to service classes (e.g., AuthenticationService) or similar. This allows the User model to remain focused on storing and managing user data.

Bad Model Example:

class UserAuthenticator; end
class UserLogin; end
Enter fullscreen mode Exit fullscreen mode

Why Are They Undesirable? 🤔

UserAuthenticator:
The name suggests it handles authentication logic rather than representing a data entity. This blurs the line between business logic and data representation, making the model harder to understand and maintain. Before you know it, you’re thinking, “So, this model seems to handle authentication logic... but also stores this data... and now it's doing even more business logic too 🤯” Instead, it’s better to use a dedicated service class or similar, keeping the User related model focused on managing user related data while making authentication logic easier to maintain.

UserLogin:

While the name may seem intuitive at first, it can invite responsibilities that are outside the model's intended scope. The name suggests a process (logging in), not a data entity, and this can lead to confusion. Models should represent data entities, not actions or processes. As a result, this name may cause developers to think UserLogin is a service object or something responsible for the login process, when it's actually a model meant to represent user data.

If you start adding behaviors like session management because they seem to align with the name ⚠️, you risk turning the model into a "god object" that tries to handle too many responsibilities. As the model takes on both data and behavior, it becomes harder to maintain and extend. Over time, this can lead to a tangled codebase where the responsibilities of the model are unclear, making it difficult to manage and debug.

3. Avoid Misleading/Imprecise Names

Choosing precise names for models is critical to maintaining clarity in your codebase.

Misleading or imprecise names can lead to confusion and introduce subtle bugs, as they fail to communicate the model’s true role or behavior. Clear naming helps developers quickly understand the purpose of a model, what data it holds, and how it interacts with other parts of the application.

Example 1: Dialog vs Modal

Consider UI components:

  • Dialog: A non-blocking window that allows users to interact with other elements in the app while it's open. Common use cases include information displays, prompts, or optional actions.
  • Modal: A blocking window that prevents the user from interacting with the rest of the application until the action within the modal is addressed. It's commonly used for critical tasks like confirming a deletion or completing an essential form.

Even though they are similar, the distinction between these two components can be important. Mislabeling a Modal as a Dialog can confuse developers and lead to incorrect assumptions about its behavior, affecting both the code and user experience.

Example 2: Consent vs Agreement

In legal contexts, Consent and Agreement have distinct meanings, but they’re often used interchangeably, which can lead to ambiguity when naming models:

  • Consent: The act of granting permission or approval for something to happen. In legal contexts, it’s typically one-sided and can be revoked at any time.
  • Agreement: A mutual understanding or contract between two or more parties, usually involving commitments or promises, and often legally binding.

When naming models related to permissions or legal terms, it’s crucial to use these terms correctly. Confusing Consent with Agreement could mislead developers and result in improper handling of user data or permissions, which might have legal consequences. Proper naming ensures that models represent the right concepts and helps maintain clarity in both the code and the business logic.

Using misleading or imprecise names for models can cause confusion, making it difficult for developers to understand their role and function. Clear, accurate names are essential for maintaining the integrity of your codebase and ensuring proper functionality. Precise naming ensures that the right concepts are represented and makes it easier to manage the application as it grows.

4. Avoid Using Verbs in Model Names

When naming models, you should often avoid using verbs(or names that ends with -ing or -er) that imply actions.

Using verbs in model names can create unnecessary confusion, as it might suggest that the model is responsible for performing an action rather than simply representing data.

Avoid:

class FeeAdder; end
Enter fullscreen mode Exit fullscreen mode

FeeAdder implies that the model is responsible for performing an action—specifically, adding a fee. However, models should represent data entities, not actions. The model name doesn’t clearly convey what the class represents, but rather what it is supposed to do. This could confuse developers about the class's purpose and make it unclear where the actual logic for adding a fee should reside.

Better:

class Fee
  # Represents a fee entity, focusing on attributes like amount, type, and associated details,
  # without being tied to the action of adding or creating the fee.
end
Enter fullscreen mode Exit fullscreen mode

The model now represents the entity itself, which could include attributes such as amount, fee type, payment schedule, or any other characteristics relevant to the fee. The action of adding or creating a fee is better handled by a separate component, such as a service object or controller, rather than in the model itself.

5. Avoid Using Vague or Overly General Names

Names like Info, Data, or Details might seem fine at first glance, but they are too vague to effectively describe the specific role of the model. These names don’t provide enough context or clarity, making it difficult for developers to understand what the model actually represents or how it fits into the system.

Avoid:

class FeeInfo
  # "Info" is too vague and doesn’t specify what kind of information this model holds.
  # It could refer to any kind of fee-related data, such as amounts, types, discounts, etc.
end
Enter fullscreen mode Exit fullscreen mode

Using generic terms like "Info" can make things unclear and lead to confusion. For example, a name like FeeInfo could mean a lot of things—whether it's about a fee's description, its discount structure, payment terms, or just a list of amounts. This vagueness makes it harder for developers to quickly understand what the model is really for, and they might make incorrect assumptions about its purpose.

This confusion can also cause headaches when it's time to extend or maintain the model. For example, if you're adding logic for fee discounts based on customer types, it's not clear if FeeInfo is the right model for that. This uncertainty can slow down both adding new features and debugging existing ones. 🛠️

Plus, names like "Info" end up making the model names longer without really helping anyone understand what it does. This bloats the codebase and makes it harder to navigate. If you have models named UserInfo, OrderInfo, and ProductInfo, for example, developers might waste time figuring out what each one actually does.

Better:

class FeeStructure
  # Indicates that the model represents the fee structure,
  # including different fee types, rules, and pricing logic.
end
Enter fullscreen mode Exit fullscreen mode

FeeStructure explicitly describes the fee-related logic, avoiding confusion. This is much more specific and directly convey the purpose of the model.


How to Name Model Methods

When naming model methods, it’s essential to choose names that clearly communicate the behavior, purpose, and intent of the method. A well-named method provides instant clarity on what it does, why it exists, and how it should be used, without requiring anyone to read through its implementation.

  • Behavior: Focuses on what the method does in terms of business logic. Does it calculate something? Retrieve data? Or modify state?
    • ✅ order.total_price → Calculates the total price, accounting for discounts and taxes.
    • ❌ order.price → Unclear whether this is the base price, discounted price, or something else.
  • Purpose: Explains why the method exists—what problem it solves or what role it plays in the system.
    • ✅ subscription.active_users → Returns only the users with an active subscription.
    • ❌ subscription.users → Too vague—does it return all users, just active ones, or even canceled ones?
  • Intent: Reflects how the method should be used—whether it's a query (retrieving data) or a command (modifying state).
    • ✅ user.deactivate! → Clearly modifies the user’s state, indicating a destructive action.
    • ❌ user.set_inactive → The intent is unclear—does it save the change or just set an instance variable?

In my experience, if you struggle to name a method, it’s often a sign that the model name itself might be unclear.

A well-named method should feel natural and intuitive when read aloud in the context of the model. If you find yourself hesitating while naming a method, it could mean that the model’s name doesn’t accurately reflect the entity it represents.

For example:

✅ User.admin? → Makes sense because "User" is a well-defined entity, and asking if a user is an admin is a natural question to ask.

❌ UserAdding.admin? → Feels unnatural because UserAdding suggests an action, not an entity. What does it mean for an "adding process" to be an admin? This awkwardness signals that the model itself might need renaming.

I tend to find that this issue often appears when models are named after processes or actions (UserCreation, FeeProcessing, SubscriptionHandling) instead of the actual domain entity (User, Fee, Subscription). When the model is not well-named, every method added to it will feel slightly off, leading to unclear and awkward method names.

A good rule of thumb: If you struggle to name a method concisely, reconsider the model’s name first. A well-named model leads to method names that feel obvious and easy to understand.

With this in mind, let’s dive deeper into specific strategies for naming model methods clearly and concisely. The following tables will walk you through different types of methods and provide examples of good and bad naming practices.

1. Naming Methods for Getting Data

Methods that retrieve data should clearly express what they return. They should be descriptive, but not redundant, avoiding unnecessary prefixes like get_. The method name should make it clear whether it returns a single item or a collection.

Good Example Bad Example Why?
order.total_price order.price "price" is too vague—does it mean the base price, discounted price, or final total? "total_price" is explicit.
company.employees company.get_employees "get_" is unnecessary—method calls should be declarative, not imperative. "employees" is enough.

2. Naming Methods for Updating Data

Methods that modify data should describe what they change, not how they do it. Avoid vague or generic names like modify, which don’t specify what is being changed. If a method modifies data, consider adding ! to signal a state change.

Good Example Bad Example Why?
user.update_email_address! user.modify_email "modify" is ambiguous—it doesn’t specify whether it updates, deletes, or formats the email.
subscription.cancel! subscription.change_status "change_status" is vague—it could mean activating, pausing, or canceling. "cancel!" is explicit.

3. Naming Methods That Return Boolean Values

Boolean methods should ask a clear question and return true or false. Use ? at the end of the method to indicate a predicate. Avoid method names that suggest they return a status, object, or string rather than a boolean value.

Good Example Bad Example Why?
user.admin? user.is_admin? "is_" is redundant—Ruby convention prefers ? methods for predicates.
order.paid? order.payment_status "payment_status" sounds like it returns a string or object, not just true or false.
product.in_stock? product.has_stock? "has_stock?" is unnecessary—"in_stock?" is more natural and aligns with human language.

4. Avoiding Implementation Details in Method Names

Method names should focus on what they represent, not how they work. Avoid tying methods to specific implementations, like database queries or API calls, since these details can change over time. Instead, name methods based on their domain meaning.

Good Example Bad Example Why?
user fetch_user_from_db "fetch_user_from_db" ties the method to one specific implementation. What if users are cached?
notifications retrieve_notifications_from_redis "retrieve_notifications_from_redis" locks the method to a specific storage. "notifications" allows flexibility.

5. Clearly Differentiating Between Queries and Commands

Queries return data and should never modify state, while commands modify state and should include "!" if they have potentially dangerous side effects. Mixing these two can cause unexpected behavior—or worse, accidentally change data when you only meant to retrieve it.

Imagine asking a waiter:
🧑‍💼 "Do you have a blueberry muffin?"
👨‍🍳 "Yes, and I just placed an order for you! You will be charged $12!"

Wait, you just wanted to check, right? Not commit to an order! This is what happens when a method that looks like a query (order.cancel) actually modifies state—it surprises developers and leads to unintended changes.

Good Example Bad Example Why?
order.cancel! order.cancel Commands that modify state should end in ! to signal they change data.
subscription.renew! subscription.renew_subscription "renew_subscription" is unnecessarily long—"renew!" is already clear within the context of a subscription.
user.active? user.is_active Queries should end with ? to indicate they return a boolean.
cart.total_price cart.calculate_total_price "calculate_total_price" suggests an action rather than a simple lookup—queries should describe what they return, not how.
product.discontinued? product.check_if_discontinued "check_if_discontinued" sounds like an action—a simple ? method is clearer.

6. Handling Plurality Correctly

Use plural names for collections and singular names for single objects. Avoid redundant terms like _list, _records, or _collection, as it’s already understood that a method returning multiple objects is an array or collection.

Good Example Bad Example Why?
company.employees company.employee_list "employee_list" adds unnecessary verbosity—"employees" is expected to be a collection.
user.friends user.friend_records "friend_records" makes it sound like a raw database query instead of a meaningful method.

Wrapping Up:

Naming is a small but powerful aspect of software development in rails that greatly impacts code clarity and maintainability. The process of naming might take time to get used to, but it’s a goodd investment in cleaner, more maintainable code 😎. With thoughtful naming practices, your Rails applications will not only function well but also be easier to scale and collaborate on 😌

Top comments (2)

Collapse
 
tylerlwsmith profile image
Tyler Smith

There's lots of good stuff in this post that's applicable well beyond Rails. Great job!

Collapse
 
moriort profile image
Moriort

Gooooood! thanks