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

Pick one word per concept, then use it everywhere

If your domain has a "customer", do not call them a customer in the database, a user in the controller, an account in the email service, and a member in the analytics event. Each translation costs the reader a small mental jump, and those jumps add up across a feature.

Domain language is consistent when you can search the codebase for customer and find every place customer logic lives — the column, the model, the method, the route, the event, the log line.

Before

Two developers wrote two halves of the same feature. Both speak about the same concept, but they used different words.

<?php

class UserController
{
    public function show(int $id): array
    {
        $member = $this->memberRepository->findById($id);
        $account = $this->accountService->loadFor($member);
        $customer = $this->billingClient->getCustomer($account['stripe_id']);

        return [
            'user_id' => $member['id'],
            'account_email' => $account['email'],
            'customer_status' => $customer['status'],
        ];
    }
}
CREATE TABLE users (
    id INT PRIMARY KEY,
    member_number VARCHAR(32),
    account_email VARCHAR(255),
    customer_status VARCHAR(32)
);
events:
    - subscriber.signed_up
    - account.activated
    - customer.suspended

The same person is, in different places: a User, a Member, an Account, a Customer, and a Subscriber. Every reader has to learn that those words map to the same thing — and that mapping is unwritten.

After

The team picked one word — customer — and used it everywhere.

<?php

class CustomerController
{
    public function show(int $customerId): array
    {
        $customer = $this->customerRepository->findById($customerId);
        $billingProfile = $this->billingClient->getProfile($customer['stripe_id']);

        return [
            'customer_id' => $customer['id'],
            'customer_email' => $customer['email'],
            'customer_status' => $billingProfile['status'],
        ];
    }
}
CREATE TABLE customers (
    id INT PRIMARY KEY,
    customer_number VARCHAR(32),
    customer_email VARCHAR(255),
    customer_status VARCHAR(32)
);
events:
    - customer.signed_up
    - customer.activated
    - customer.suspended

The reader can now grep for customer and find the controller, the table, the events, and the logs without translating anything in their head.

Why this version is better

  • one search finds every place a concept lives
  • bug reports use the same word as the code, not a translation of it
  • product, support, and engineering can talk about the same thing without lookup tables
  • new joiners learn one vocabulary, not three overlapping ones

How translation creeps in

Layer Where the wrong word slips in
Database columns named for old features (member_number left over from the original product)
ORM / Eloquent a model named User mapping to customers table
HTTP routes /api/users/{id} for what the code calls a customer
Background jobs SendMemberWelcomeEmail queue job for a Customer
Events / webhooks subscriber.created event for a Customer
Logs and metrics member_signup_total counter for customer.signed_up event
Documentation and PRs "the new account flow" referring to the customer registration code

Each of these on its own looks small. Stack three of them together and the feature becomes hard to follow.

Practical habits

  • Pick the word with the business, not in code review. The product team already says one thing more often than the others — use that one.
  • Keep a tiny glossary in the repo (a GLOSSARY.md of 10–30 lines is enough). When two words exist for one concept, the glossary records the chosen one.
  • Match the database. If you cannot rename the column today, prefer the column's word in code. A consistent "wrong" word is better than two competing words.
  • Resist plural alternatives. User and Customer are not synonyms — pick one and treat the other as a smell.
  • Translate only at the edge. If a third-party API uses a different word (Stripe says Customer, your domain says Member), translate at the integration boundary, not throughout the codebase.

A small consistency smell test

Pick a core concept in your codebase. Then ask:

  1. How many different words are used for it across the code, the database, the API, and the events?
  2. Could a new developer guess the word from one place and use it in another?
  3. Does the bug tracker use the same word as the code?

If the answers are "more than one", "no", and "no", the domain language is leaking — and every feature touching that concept will pay a small tax until it is fixed.

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