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:
$arris a list of what?$item['s']is which field?$item['s'] === 'p'means what in business terms?$listand$roware 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
$invoiceRepositorytells 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 (
$reqis 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