Originally published on decentphp.dev — part of the Writing decent PHP course. View this lesson on the original site.
Abbreviate only when the team genuinely says it out loud
Abbreviations save typing, which is a tiny gain. Bad abbreviations cost reading, which is a much larger loss. The trade only works when the short form is so common that every reader instantly expands it.
The rule of thumb is simple: if you would not say it out loud in a meeting, do not type it in code.
Nobody says "the U.S.R. signed up". They say "the user signed up". So the variable is $user, not $usr.
Everyone says "the H.T.T.P. request". So HttpRequest is fine.
That single test handles 80% of the decisions you have to make in this area.
Before
A function full of saved keystrokes. Every variable looks like a half-typed word.
<?php
function chgSubsForUsrInOrg(array $usr, array $org, array $cfg, $ctx): bool
{
$sub = $usr['sub'];
$tx = $cfg['tx_typ'];
$req = $ctx->req();
$tmp = $req['amt'] * $cfg['mult'];
if ($tmp > $org['lim']) {
return false;
}
return $usr['stat'] === 'act';
}
Every reader has to do mental expansion:
chg= change? charge? check?Subs= subscriptions? submissions? subscribers?Usr= user (probably)Org= organisationcfg= configctx= context (Symfony? Laravel? something else?)tx_typ= transaction type, but written like a SQL columntmp= a temporary what?lim= limit (of what?)stat= status, not statistic
The function "saves" maybe twenty keystrokes and costs every reader twenty seconds.
After
The same function with the abbreviations expanded. The only short forms left are universal ones the team would actually say out loud.
<?php
function chargeSubscriptionForUserInOrganisation(
array $user,
array $organisation,
array $billingConfig,
BillingContext $context
): bool {
$subscription = $user['subscription'];
$request = $context->httpRequest();
$totalInPence = (int) round($request['amount_in_pence'] * $billingConfig['multiplier']);
if ($totalInPence > $organisation['monthly_limit_in_pence']) {
return false;
}
return $user['status'] === 'active';
}
The function name reads as a sentence. The variables read as words. The only "abbreviation" left (Http) is one nobody ever expands in conversation.
Why this version is better
- new joiners do not need a private dictionary to read the function
- search works (
grep subscriptionfinds every place subscriptions live;grep subfinds chaos) - the function name itself is now a one-line description of what it does
- review comments can talk about the rule (
monthly_limit_in_pence) instead of asking "what islim?"
Abbreviations that earn their keep
| Acronym | Why it survives | How to write it in PHP |
|---|---|---|
HTTP, HTTPS |
said as letters; universal | HttpRequest, HttpsClient |
URL, URI |
spoken as letters | $signupUrl, UriParser |
JSON, XML, CSV, HTML, CSS, PDF |
said as letters or pronounced as one syllable | JsonEncoder, HtmlRenderer |
SQL |
universal in any DB code | SqlQueryBuilder |
API, SDK |
universal | StripeApiClient |
UUID, ID |
universal | $userId, $invoiceUuid |
IO |
systems-level convention | IoException |
DB |
acceptable in DB-only contexts | $dbConnection is fine; $dbStuff is not |
OS, CPU, RAM |
universal hardware terms | OsVersion, CpuUsage |
Note the casing: when an acronym is used as part of a class or variable name, treat it as a normal word (HttpRequest, not HTTPRequest). This is the PSR convention and it keeps multi-word names readable (XmlHttpRequest, not XMLHTTPRequest).
Abbreviations that almost never earn their keep
| Bad | Why it fails | Better |
|---|---|---|
usr |
nobody says "U.S.R." | user |
req |
could mean request, requirement, requeue | request, requirement |
res |
request? response? result? reservation? | response, result |
cfg, conf |
universal concept, not a universal abbreviation | config |
ctx |
leaks framework jargon into business code | context, or name the specific context |
tmp, temp |
hides what kind of "temporary" | name the actual value |
mgr, mngr |
"manager" was already vague (lesson 2.6) — abbreviating doubles the problem | rename the class |
lst, arr, obj, dict, var |
type-shape names (lesson 2.1) wearing a disguise | name by intent |
stat |
status or statistic? | status, statistic |
loc, qty, addr |
save 3 characters, cost a translation | location, quantity, address |
Domain-specific 3-letter codes (pmt, inv, cst) |
only readable to the original author | payment, invoice, customer |
Team-specific acronyms
Most teams have one or two of these — KYC, MRR, LTV, SLA, PII, an internal product code. They are fine in code if they pass two tests:
- Anyone with onboarding completed knows what it means.
- It is in the repo glossary (see lesson 2.8).
If a team-specific acronym fails either test, expand it in code and add it to the glossary the first time it appears.
Single-letter names
Single letters are fine in two narrow cases:
- a tight, obvious loop counter —
for ($i = 0; $i < $n; $i++) - a closure parameter where the meaning is obvious from one line above —
array_filter($users, fn ($u) => $u['active'])is acceptable;fn ($user) => $user['active']is better
Outside those, single letters cost more than they save. $x, $y, $d, $n in business logic are almost always a missed naming opportunity.
A small abbreviation smell test
For every short name in your code, ask:
- Would I say this exact short form out loud in a stand-up?
- Could it expand into more than one English word?
- Would a new joiner need to ask what it means?
If the answer to (1) is no, or to (2) or (3) is yes, expand the name. The few extra characters are the cheapest part of the entire system.
From Writing decent PHP — Naming variables, functions, classes, and files well