Originally published on decentphp.dev — part of the Writing decent PHP course. View this lesson on the original site.
What "start small" actually means
Starting small does not mean avoiding real refactors.
It means proving your approach on a tiny, low-risk slice first.
Pick a small function where behaviour is obvious, apply one cleanup pattern, and verify nothing changes.
Then apply that same pattern to a bigger function with more moving parts.
Ideally, you should be working against a unit test while doing this.
If a test does not exist, create one before you start making small refactors.
Small, safe slice first
Before
<?php
function sendReminder($invoice, $mailer): void
{
if (($invoice['overdue'] ?? false) === true) {
if (($invoice['email'] ?? '') !== '') {
$mailer->send($invoice['email'], 'Invoice reminder');
}
}
}
After
<?php
function sendReminderForOverdueInvoice(array $invoice, $mailer): void
{
if (($invoice['is_overdue'] ?? false) !== true) {
return;
}
$email = (string) ($invoice['customer_email'] ?? '');
if ($email === '') {
return;
}
$mailer->send($email, 'Invoice reminder');
}
Then scale the same pattern
Use the exact same moves (clear names + early exits) on a function with more logic.
Before
<?php
function proc($in, $repo, $mailer): void
{
if (($in['ok'] ?? false) === true) {
if (($in['email'] ?? '') !== '') {
$repo->save($in);
if (($in['remind'] ?? false) === true) {
$mailer->send($in['email'], 'Invoice reminder');
}
}
}
}
After
<?php
function processInvoiceUpdate(array $input, InvoiceRepository $invoiceRepository, Mailer $mailer): void
{
if (($input['is_valid'] ?? false) !== true) {
return;
}
$email = (string) ($input['customer_email'] ?? '');
if ($email === '') {
return;
}
$invoiceRepository->save($input);
if (($input['send_reminder'] ?? false) === true) {
$mailer->send($email, 'Invoice reminder');
}
}
Why this approach works
- you learn the pattern on a low-risk change first
- you reduce the chance of breaking behaviour in bigger refactors
- you add type hints alongside clarity improvements, so intent is explicit at the function boundary
- you build confidence and repeatability, not one-off clever rewrites
From Writing decent PHP — What decent PHP looks like