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

Naming by intent, not by type

A name should tell the reader what the value means in the business, not what shape it has in PHP.
$arr, $data, $obj, and $item describe the type. $pendingApprovals, $signupInput, and $expiredInvoice describe the intent.

When names describe intent, the reader understands the code without scrolling. When names only describe the type, they have to read every surrounding line just to work out what they are looking at.

Before

Type-shaped names. The reader has to track what each variable actually means as they go.

<?php

function process(array $arr, $repo): void
{
    $list = [];

    foreach ($arr as $item) {
        if ($item['s'] === 'p' && $item['a'] > 0) {
            $list[] = $item;
        }
    }

    foreach ($list as $row) {
        $repo->save($row);
    }
}

You have to keep translating in your head:

  • $arr is a list of what?
  • $item['s'] is which field?
  • $item['s'] === 'p' means what in business terms?
  • $list and $row are filtered versions of the same thing, but the names do not say so.

After

Intent-shaped names. The reader does not need to translate anything.

<?php

const STATUS_PENDING = 'pending';

function persistPendingInvoices(array $invoices, $invoiceRepository): void
{
    $pendingInvoices = [];

    foreach ($invoices as $invoice) {
        if ($invoice['status'] === STATUS_PENDING && $invoice['amount'] > 0) {
            $pendingInvoices[] = $invoice;
        }
    }

    foreach ($pendingInvoices as $pendingInvoice) {
        $invoiceRepository->save($pendingInvoice);
    }
}

The behaviour is identical, but you can now explain the function in one sentence: "save every pending invoice with a non-zero amount".

Why this version is better

  • the function name describes the job, not the implementation
  • the loop variables make the domain obvious ($invoice, $pendingInvoice)
  • the magic string 'p' becomes a named constant with an explicit meaning
  • the parameter $invoiceRepository tells you what is being passed in, not just that it is "a thing"
  • code review can focus on the rule (amount > 0) instead of decoding names

Quick rules of thumb

  • prefer the noun the business uses (invoice, signup, subscription) over generic words (data, record, item)
  • name a collection after what it contains ($pendingInvoices, not $list)
  • name a single element after the singular form of the collection ($invoice, not $row)
  • avoid abbreviations unless the team genuinely uses them out loud ($req is rarely worth the saved characters)
  • if a name needs a comment to explain it, the name is the problem

A small naming smell test

Read the variable name out loud and ask: if a new developer joined the team tomorrow, would they know what this is without opening the function?

If the answer is "only if they read the surrounding code first", the name is doing work the name should be doing for them.

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