PHPレガシーコード改善で安全にリファクタリングする判断基準

PHP レガシーコード 改善で巨大関数と密結合を整理する図

PHP レガシーコード 改善は全面刷新から始めない

PHP レガシーコード 改善で難しいのは、古いコードそのものではありません。巨大関数、密結合、テスト不足が重なり、どこを触ると何が壊れるか読みにくいことです。

結論から言うと、最初にやるべきことは全面刷新ではありません。まず既存の振る舞いを固定します。そのうえで、影響範囲が狭い箇所から小さく改善します。

この記事では、既存PHPシステムの保守・改修を担当する経験者向けに、レガシーコードを安全に改善する判断基準を整理します。また、Laravel移行を検討するときに、どこまで既存コードを残すべきかも扱います。

PHPレガシーコードで改善前に見るべき影響範囲

まず確認したいのは、コードの見た目ではなく影響範囲です。1つの関数が長くても、利用箇所が少なく、業務上の分岐が明確なら改善しやすい場合があります。

一方で、短い関数でもDB更新、メール送信、外部API連携、セッション操作を同時に行っているなら注意が必要です。構造を変えた瞬間に、業務上の振る舞いが変わる可能性があります。

確認項目見る理由改善前の判断
呼び出し元修正の波及範囲を読むため共通関数ほど慎重に進める
DB更新データ不整合を避けるため更新順序とトランザクションを確認する
外部連携失敗時の振る舞いが変わりやすいため再送、ログ、例外処理を見る
画面出力表示崩れや仕様差分が出やすいため代表画面を確認対象にする
セッションログイン状態や権限に影響するため認証・認可の境界を確認する

なお、PHPの関数や言語仕様を確認するときは、まずPHP公式マニュアルのFunctionsを見るのが安全です。古いPHPコードでは、暗黙の型変換やグローバル変数の使い方が不具合の原因になることがあります。

巨大関数は処理順序を守って小さく分ける

巨大関数を見つけると、すぐメソッド抽出したくなります。ただし、処理順序に意味がある場合は、単純な分割でも振る舞いが変わることがあります。

例えば、注文処理に入力チェック、在庫確認、割引計算、DB保存、通知が混ざっている場合を考えます。まずは処理の流れを残したまま、名前を付けられる塊を切り出します。

// 改善前: 入力確認、計算、保存、通知が1つの関数に混ざっている
function submit_order(array $request): void
{
    if (empty($request['items'])) {
        throw new InvalidArgumentException('items is empty');
    }

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

    if (($request['member_rank'] ?? '') === 'gold') {
        $total = (int) floor($total * 0.9);
    }

    save_order($request['user_id'], $request['items'], $total);
    send_order_mail($request['user_id']);
}

この状態では、割引計算を直すだけでも保存や通知の影響を意識する必要があります。そのため、最初は計算処理など副作用の少ない部分から分けると安全です。

// 改善例: 副作用の少ない計算処理から切り出す
function calculate_order_total(array $items, string $memberRank): int
{
    $total = 0;
    foreach ($items as $item) {
        $total += $item['price'] * $item['quantity'];
    }

    if ($memberRank === 'gold') {
        return (int) floor($total * 0.9);
    }

    return $total;
}

このように、DB更新や通知より先に計算ロジックを切り出すと、テストを追加しやすくなります。つまり、巨大関数の改善では「どこが汚いか」より「どこなら安全に分離できるか」を見ます。

テスト追加はPHPレガシーコード改善の安全網になる

テストがない既存PHPシステムでは、最初から網羅的なテストを目指すと止まりやすくなります。そこで、変更したい処理の代表ケースから固定します。

PHPUnitの書き方は、公式のWriting Tests for PHPUnitで確認できます。実務では、正常系、境界値、異常系を少数でも先に用意すると、リファクタリング後の差分を判断しやすくなります。

use PHPUnit\Framework\TestCase;

final class OrderTotalTest extends TestCase
{
    public function test_calculates_total_for_gold_member(): void
    {
        $items = [
            ['price' => 1000, 'quantity' => 2],
            ['price' => 500, 'quantity' => 1],
        ];

        $actual = calculate_order_total($items, 'gold');

        $this->assertSame(2250, $actual);
    }
}

もちろん、すべてのレガシーコードを単体テストにできるとは限りません。DBや画面に強く依存している場合は、まず手動確認の手順やログで振る舞いを固定します。その後、切り出した純粋な計算や判定から自動テストを増やします。

密結合をほどくときは依存の向きを変える

PHPレガシーコードでは、関数の中から直接DB、メール、セッション、グローバル変数に触ることがあります。この状態では、1つの処理をテストするために周辺の環境まで必要になります。

そこで、まず副作用を持つ処理を境界として見ます。例えば、メール送信そのものは残しつつ、送信する内容を作る処理だけ分けます。さらに進めるなら、メール送信をインターフェースや小さなサービスに寄せます。

密結合の例起きる問題改善の方向
関数内で直接SQLを組み立てる業務判断と永続化が混ざる取得・保存処理を分ける
グローバル変数を参照する入力条件が読みにくい引数として渡す
セッションを直接更新する画面遷移と状態変更が絡む更新タイミングを明示する
メール送信を直接呼ぶテスト時に副作用が出る本文生成と送信を分ける

ただし、分離しすぎると読むファイルが増えます。改善の目的は、抽象化を増やすことではありません。変更頻度が高い処理やテストしたい処理を、扱いやすい単位へ分けることです。

Laravel移行は改善範囲を決めてから判断する

PHPレガシーコード改善では、Laravel移行が選択肢になることがあります。ルーティング、バリデーション、DI、Eloquent、キューなどを使えるため、保守性を上げやすい場面はあります。

一方で、既存システムを一気にLaravelへ載せ替えると、業務仕様の再現が最大のリスクになります。Laravelのバージョン差分や移行時の破壊的変更は、公式のLaravel Upgrade Guideのような一次情報で確認しながら進める必要があります。

実務では、まず画面単位、API単位、バッチ単位で切り出せるかを見ます。既存コードをすべて捨てるのではなく、境界を決めて段階的に移す方が安全です。

移行方針向いている状況注意点
部分改善既存仕様が複雑でリリース頻度が高い古い構造が残り続ける
画面単位の移行画面ごとの責務が比較的分かれている認証やセッション連携を確認する
API単位の移行フロントエンドや外部連携と境界があるレスポンス仕様の互換性を守る
全面刷新仕様整理と予算、検証期間が確保できる再現漏れと移行期間の長期化に注意する

つまり、Laravel移行は目的ではなく手段です。現在の課題が巨大関数なのか、テスト不足なのか、認証やDB設計の老朽化なのかを分けてから判断します。

PHP レガシーコード 改善でレビューされやすい観点

保守改善のレビューでは、コードが短くなったかだけでは判断しません。むしろ、安全に変えられているか、既存仕様を壊していないか、次の変更が楽になるかを見ます。

  • 変更前の振る舞いをテスト、ログ、手順で確認できるか
  • 機能追加とリファクタリングを同じ差分に混ぜていないか
  • 巨大関数を処理目的ごとに説明できるか
  • 密結合をほどくことで依存関係が逆に増えていないか
  • DB更新、外部API、メール送信の順序を変えていないか
  • Laravel移行をする場合、互換性と段階移行の境界があるか
  • レビューコメントで改善意図と影響範囲を説明できるか

特に、テストなしの大きな書き換えは危険です。改善の意図が正しくても、リリース後に既存仕様が崩れると保守改善の信頼を失います。

PHPレガシーコード改善でよくある質問

テストがないPHPコードは直してはいけませんか?

直してはいけないわけではありません。ただし、いきなり大きく直すのは避けます。まず代表的な入力、出力、画面表示、DB更新結果を固定します。その後、切り出した処理からPHPUnitなどで自動テストを追加します。

巨大関数はすべて分割すべきですか?

すべて分割する必要はありません。変更頻度が低く、影響範囲も限定的なら、無理に触らない判断もあります。一方で、何度も修正する処理、テストしたい処理、障害が多い処理は分割の優先度が高くなります。

Laravel移行をすればレガシー問題は解決しますか?

Laravel移行だけでは解決しません。業務ロジックの責務、テスト、DB設計、例外処理が曖昧なまま移すと、新しいLaravelプロジェクトの中に古い問題が残ります。まず課題を分け、移行する範囲を決めることが重要です。

共通化は早めに進めた方がよいですか?

急ぎすぎない方が安全です。見た目が似ているコードでも、業務上の意味が違う場合があります。同じ理由で変わると確認できてから共通化すると、分岐だらけの共通関数を避けやすくなります。

まとめ:PHP レガシーコード 改善は影響範囲を小さく保つ

PHPレガシーコード改善では、古いコードを一気にきれいにすることを目的にしません。既存の振る舞いを守りながら、次の変更を安全に入れられる状態へ近づけることが目的です。

まずは影響範囲を確認します。次に、巨大関数の中から副作用の少ない処理を切り出します。さらに、テスト追加、密結合の整理、Laravel移行の範囲決めを進めます。この順番を守ると、改善のつもりで不具合を増やすリスクを下げられます。

PHPやLaravelの保守改善では、リファクタリング、テスト設計、影響範囲の見極めが評価されます。既存システムの改善経験やPHP/Laravel移行の経験を次の案件でどう活かすかを整理したい場合は、カジュアル面談で現在地から相談できます。

IaC INP PM PMO PMP UX Webディレクター インフラエンジニア キャリアチェンジ フロントエンドエンジニア