Naming Classes, Types, and Interfaces
A class name reveals what it's responsible for. When a class is named OrderManager, I worry. Managers tend to do everything. When a class is named OrderValidator, I know exactly what it does.
Class names are nouns. They represent things. But not all nouns are created equal.
Nouns That Represent Responsibilities
A good class name describes a single, clear responsibility.
// Clear, focused responsibilities
class PaymentProcessor { } // Processes payments
class EmailSender { } // Sends emails
class PasswordHasher { } // Hashes passwords
class OrderValidator { } // Validates orders
class InvoiceGenerator { } // Generates invoices
class SessionManager { } // Manages sessions (scope is clear)
Each name tells you:
- What domain it belongs to
- What operation it performs
- Implicitly, what it doesn't do
Words to Avoid (Usually)
Manager
What does a manager do? Everything. That's the problem.
// Suspicious: What doesn't this do?
class UserManager {
createUser() { }
validateUser() { }
authenticateUser() { }
sendWelcomeEmail() { }
trackAnalytics() { }
updatePreferences() { }
}
This class probably violates single responsibility. Split it:
class UserRepository { } // CRUD operations
class UserValidator { } // Validation logic
class AuthenticationService { } // Login/logout
class UserNotifier { } // Emails and notifications
class UserAnalytics { } // Tracking
class PreferencesService { } // User settings
Handler
Handler implies "deals with" but doesn't say how.
// Vague
class ErrorHandler { }
// Specific
class ErrorLogger { } // Logs errors
class ErrorNotifier { } // Sends alerts
class ErrorRecovery { } // Attempts recovery
Processor
Same problem. What kind of processing?
// Vague
class DataProcessor { }
// Specific
class CsvParser { } // Parses CSV
class DataNormalizer { } // Normalizes format
class DataValidator { } // Validates content
class DataExporter { } // Exports data
When These Words Are Okay
Sometimes Manager or Handler is genuinely the right name:
// These are actually about managing
class ConnectionPoolManager { } // Manages a pool of connections
class CacheManager { } // Manages cache lifecycle
class TransactionManager { } // Manages transaction boundaries
// This is actually about handling
class WebhookHandler { } // Handles incoming webhooks (entry point)
class ExceptionHandler { } // Framework-level exception handling
The test: can you explain what the class manages or handles in one sentence? If yes, the name might be fine. If no, split it.
Entity vs. Service Naming
Entities: Pure Nouns
Entities represent domain objects:
class User { }
class Order { }
class Product { }
class Invoice { }
class ShoppingCart { }
They hold data and domain logic related to that data.
Services: Noun + Action Hint
Services perform operations, often on entities:
class OrderService { } // Operations on orders
class PaymentGateway { } // Interface to payment provider
class ShippingCalculator { } // Calculates shipping costs
class TaxCalculator { } // Calculates taxes
Repository: Data Access
Repositories handle persistence:
class UserRepository { } // CRUD for users
class OrderRepository { } // CRUD for orders
Interface Naming Conventions
Different languages have different conventions. Pick one and be consistent.
Prefix with I (C#, some TypeScript)
interface IUserRepository { }
interface IPaymentProcessor { }
class UserRepository implements IUserRepository { }
No prefix, implementations are "Impl" (Java-style)
interface UserRepository { }
class UserRepositoryImpl implements UserRepository { }
No prefix, implementations are specific (preferred in modern TypeScript)
interface PaymentProcessor { }
class StripePaymentProcessor implements PaymentProcessor { }
class PayPalPaymentProcessor implements PaymentProcessor { }
Suffix with "Interface" (explicit)
interface UserRepositoryInterface { }
My Recommendation
Use implementation-specific names when you have multiple implementations:
interface Cache { }
class RedisCache implements Cache { }
class InMemoryCache implements Cache { }
Use simple names when there's only one implementation:
// No need for interface complexity if there's only one
class EmailService { }
Don't create interfaces for the sake of interfaces.
Type Aliases and Domain Types
Type aliases can encode domain rules:
// Weak: string could be anything
function sendEmail(to: string, from: string, subject: string) { }
// Strong: types encode meaning
type EmailAddress = string;
type Subject = string;
type Body = string;
function sendEmail(to: EmailAddress, from: EmailAddress, subject: Subject) { }
Even better with branded types:
type UserId = string & { readonly brand: unique symbol };
type OrderId = string & { readonly brand: unique symbol };
// Now these can't be mixed up
function getOrdersForUser(userId: UserId): Order[] { }
Avoid Redundant Suffixes
In a folder called models/, you don't need:
models/
UserModel.ts // "Model" is redundant
OrderModel.ts
Just use:
models/
User.ts
Order.ts
The context (folder) provides the information.
Consistency Across the Codebase
Whatever conventions you choose, apply them everywhere:
- If you use
UserService, useOrderService, notOrderHelper - If you use
UserRepository, useOrderRepository, notOrderDao - If you suffix interfaces with
Interface, do it everywhere
Inconsistency creates cognitive load. Every time someone encounters a new convention, they have to figure out if it means something different.
Key insight: Class names should describe a single responsibility. Avoid vague names like Manager, Handler, Processor unless the class genuinely manages/handles/processes exactly one well-defined thing. When you can't name a class simply, it's probably doing too much.