Originally published on decentphp.dev — part of the Writing decent PHP course. View this lesson on the original site.

A class name should describe a single concept

A class name is a noun. The reader should be able to say "an instance of this is a ___" and have a clear answer.

When a class is named Manager, Helper, Util, or Processor, that sentence becomes "an instance of this is a manager". A manager of what? Doing what? With which dependencies?

Vague class names are usually a symptom of a class that does too many things. Better names force you to name the responsibility, and that often reveals the class should be split.

Before

Each class compiles, but the names hide what they actually do.

<?php

class UserManager
{
    public function createUser(array $input): void { /* validates + saves + sends welcome email */ }
    public function suspendUser(int $userId): void { /* updates status + logs audit + revokes sessions */ }
    public function check(int $userId): bool { /* checks if subscription is active */ }
}

class OrderHelper
{
    public function getTotal(array $items): int { /* calculates total in pence */ }
    public function format(array $order): string { /* renders order as HTML */ }
    public function send(array $order): void { /* posts to fulfilment API */ }
}

class DataProcessor
{
    public function run(array $records): array { /* transforms and writes to database */ }
}

Reading the class names alone, you have no idea:

  • what UserManager actually owns — auth? billing? profile?
  • whether OrderHelper reads, writes, or talks to a remote system
  • what DataProcessor even does — "process" and "data" together carry zero information

After

The names describe a single, specific concept. Once they do, the methods that do not fit become obvious — and tend to move out into their own class.

<?php

final class UserRegistration
{
    public function register(array $input): void { /* validates + saves + sends welcome email */ }
}

final class UserSuspension
{
    public function suspend(int $userId): void { /* updates status + logs audit + revokes sessions */ }
}

final class SubscriptionStatus
{
    public function isActive(int $userId): bool { /* checks if subscription is active */ }
}

final class OrderTotalCalculator
{
    public function calculateInPence(array $items): int { /* calculates total in pence */ }
}

final class OrderHtmlRenderer
{
    public function render(array $order): string { /* renders order as HTML */ }
}

final class FulfilmentApiClient
{
    public function submitOrder(array $order): void { /* posts to fulfilment API */ }
}

The class names now act as section headers in the codebase. You can scan a directory and predict what lives where without opening a single file.

Why this version is better

  • the class name describes one concept, so the responsibility is obvious
  • "where do I add billing logic?" stops being a guess
  • splitting UserManager into three smaller classes makes each one easier to test
  • generic suffixes (Manager, Helper) are replaced with words that say what the class does

Class naming patterns that pull their weight

Suffix Use it when Example
Repository reads/writes a single aggregate to storage InvoiceRepository, UserRepository
Factory builds instances of a value object or entity OrderFactory, JwtTokenFactory
Builder step-by-step construction of a complex object EmailMessageBuilder
Validator checks input and reports errors SignupInputValidator
Calculator derives a value from inputs OrderTotalCalculator, ShippingCostCalculator
Renderer / Formatter turns data into a presentation form OrderHtmlRenderer, MoneyFormatter
Client talks to a specific external system StripeClient, FulfilmentApiClient
Policy makes a yes/no business decision RefundPolicy, AccessPolicy
Mapper converts between two shapes OrderToInvoiceMapper
Listener / Subscriber reacts to events OrderShippedListener

These suffixes earn their keep because they tell you both the responsibility and the role in the system.

Suffixes to be careful with

  • Service — fine in moderation, but tends to become a bucket. If you cannot describe what the service does in one short sentence, the name is hiding something.
  • Manager — almost always vague. Replace with a verb-noun describing the actual responsibility.
  • Helper, Util, Utility, Common, Misc — bins for unrelated functions. If a class is named after a bin, it will be filled like one.
  • Handler, Processor — see lesson 2.4: process and handle are vague verbs and the same applies to class names built on them.
  • Data, Info — these are nouns of nothing. UserData and User should not both exist.

A small class smell test

Try to finish this sentence about your class in one short, concrete phrase:

"An instance of this class is a __________ that __________."

  • UserRegistration → "a user registration that turns input into a saved user with a welcome email" — clear
  • UserManager → "a user manager that... manages users" — useless
  • DataProcessor → "a data processor that processes data" — definitionally empty

If you cannot finish the sentence with one specific noun and one specific verb, the class is doing too much, the name is too vague, or both.

From Writing decent PHP — Naming variables, functions, classes, and files well