Does your company have a legacy PHP codebase? If you recognise any of the following, you probably do:

  1. PHP version 5.3 to 7.0
  2. Lack of documentation and/or application knowledge
  3. Struggle to keep the application alive — poor performance, constant firefighting
  4. No matter how much work is done, nothing feels like it moves forward
  5. There is one person on the tech team who is the "source of all knowledge"

You may be convinced the problem stems from your tech team focusing on the wrong things and fixing obscure issues you don't fully understand. It's frustrating. But what you're experiencing is the legacy trap — and it's a structural problem, not a personnel one.

The central dilemma

You're facing a choice: split the team and build something new from scratch, or focus the whole team on refactoring what already exists. Either path means a slowdown or halt in feature delivery. Meanwhile, errors keep coming in, targets are being missed, and the pressure mounts.

This is exactly the kind of situation where having an outside perspective helps — not to replace your team, but to give them a clear, structured path forward.

Breaking the cycle: three steps

Step 1: Codebase analysis

Before any decisions are made, you need to understand what you're actually dealing with. Gut feel isn't good enough here — you need data.

Run Migrator to get a structured picture of your codebase's upgrade complexity:

composer require kerrialn/migrator
vendor/bin/migrator analyse

This produces a report showing PHP compatibility issues, dependency state, and upgrade complexity. It frames the discussion: either the application is beyond saving (in which case the decision to rebuild is now data-driven, not emotional) or it's salvageable, with a clear list of what needs to be done.

Step 2: Set a clear goal

Based on the analysis, set a specific and achievable objective with smaller milestones leading toward it. The goal might be to upgrade to a modern PHP version, introduce automated refactoring tooling, migrate from raw SQL to an ORM, or some combination. Each milestone should be independently deployable and measurable.

Step 3: Break the cycle

There's a reason the application got into this state. You can't fix a legacy codebase without changing some behaviours. This is usually the most difficult step, because change is hard — especially in teams that have spent years in survival mode.

Breaking the cycle means:

  • Setting up automated quality checks (PHPStan, Rector, ECS) and enforcing them in CI
  • Establishing code review processes that actually gate on quality
  • Removing the bottlenecks that make knowledge tribal

The "oracle" problem

In most legacy situations, there is one developer who holds disproportionate knowledge. This creates a dangerous dependency — for the business, because any disruption to that person disrupts everything; and for the team, because knowledge that lives in one person's head isn't in the codebase where it belongs.

As the incremental improvements mount up and the application becomes more structured and documented, you'll notice a shift. Knowledge that used to be trapped in one person starts living in the code, in the tests, in the tooling. Other team members can contribute and engage because they can understand what's there.

That shift — from tribal knowledge to structural knowledge — is the most valuable outcome of a well-managed legacy migration.

Migration experience matters

Legacy migration experience saves time and money in ways that are genuinely difficult to quantify until you've been through a bad migration. Knowing which rabbit holes to avoid, which improvements compound, and which approaches make the next iteration faster — that's not in any documentation.

The goal is always the same: a codebase that any competent developer can understand, extend, and maintain. Start with the smallest possible improvement, merge it, and repeat.