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