code quality and tooling

Refactoring Basics

Refactoring means changing the structure of code without changing its behaviour. The code should become easier to read, test, extend, or maintain, but the outside result should stay the same.

This matters in PHP work because many tasks start in existing code. A junior developer must be able to improve code safely without accidentally changing business rules.

Refactoring is not a bug fix

These are different kinds of changes:

  • formatting: changing layout
  • refactoring: changing structure while preserving behaviour
  • bug fixing: changing behaviour to correct a fault
  • feature work: adding new behaviour

A pull request can contain more than one kind of change, but mixing them makes review harder.

Start with behaviour

Before refactoring, identify what must stay true.

PHP example
<?php

declare(strict_types=1);

function orderTotal(array $items): int
{
    $total = 0;

    foreach ($items as $item) {
        if ($item['active']) {
            $total += $item['price'] * $item['quantity'];
        }
    }

    return $total;
}

The behaviour is: total only active items, multiplying price by quantity, returning integer pennies.

Extract a named function

If one expression has a clear meaning, extract it into a function.

PHP example
<?php

declare(strict_types=1);

function lineTotal(array $item): int
{
    return $item['price'] * $item['quantity'];
}

function orderTotal(array $items): int
{
    $total = 0;

    foreach ($items as $item) {
        if ($item['active']) {
            $total += lineTotal($item);
        }
    }

    return $total;
}

The rule is the same, but the calculation now has a name.

Improve names

Refactoring often improves names so the next reader understands the code faster.

PHP example
<?php

declare(strict_types=1);

function activeOrderTotal(array $items): int
{
    $total = 0;

    foreach ($items as $item) {
        if ($item['active']) {
            $total += lineTotal($item);
        }
    }

    return $total;
}

The new name says that inactive items are ignored.

Use guard clauses to reduce nesting

Guard clauses can make loops easier to read.

PHP example
<?php

declare(strict_types=1);

function activeOrderTotal(array $items): int
{
    $total = 0;

    foreach ($items as $item) {
        if (! $item['active']) {
            continue;
        }

        $total += lineTotal($item);
    }

    return $total;
}

The behaviour is still the same. Inactive items are skipped.

Verify before and after

A small test or manual check protects the refactor.

PHP example
<?php

declare(strict_types=1);

$items = [
    ['active' => true, 'price' => 499, 'quantity' => 2],
    ['active' => false, 'price' => 999, 'quantity' => 1],
    ['active' => true, 'price' => 129, 'quantity' => 3],
];

echo activeOrderTotal($items);

// Prints:
// 1385

If the output changes during refactoring, stop and understand why.

Small steps are safer

Refactor in small steps:

  • format the code
  • rename one symbol
  • extract one function
  • run checks
  • commit or continue

Do not rewrite a whole file just because one helper is messy. The larger the change, the harder it is to prove behaviour stayed the same.

Red flags

Be careful when refactoring code that touches:

  • money
  • permissions
  • authentication
  • SQL queries
  • escaping and output safety
  • date and time rules
  • external APIs
  • legacy edge cases that are not documented

Refactoring these areas may still be necessary, but it needs stronger tests and review.

What to remember

Refactoring improves structure while preserving behaviour. Work in small steps, name behaviour clearly, keep tests or examples close, and separate refactoring from unrelated bug fixes when possible.

Before moving on, make sure you can extract a helper function from a small PHP calculation without changing its output.

Practice

Task: Extract A Line Total Helper

Refactor this function without changing its output.

PHP example
<?php

declare(strict_types=1);

function total(array $items): int
{
    $total = 0;

    foreach ($items as $item) {
        if ($item['active']) {
            $total += $item['price'] * $item['quantity'];
        }
    }

    return $total;
}

Requirements

  • Extract a lineTotal() helper.
  • Rename total() to make the active-item rule clearer.
  • Keep integer pennies.
  • Keep inactive items excluded.
  • Include a small usage example with expected output.
Show solution
PHP example
<?php

declare(strict_types=1);

function lineTotal(array $item): int
{
    return $item['price'] * $item['quantity'];
}

function activeOrderTotal(array $items): int
{
    $total = 0;

    foreach ($items as $item) {
        if (! $item['active']) {
            continue;
        }

        $total += lineTotal($item);
    }

    return $total;
}

$items = [
    ['active' => true, 'price' => 499, 'quantity' => 2],
    ['active' => false, 'price' => 999, 'quantity' => 1],
    ['active' => true, 'price' => 129, 'quantity' => 3],
];

echo activeOrderTotal($items);

// Prints:
// 1385

The calculation rule is unchanged. The helper names the line calculation, and activeOrderTotal() makes it clear that inactive items are skipped.