Essay

When to rewrite vs refactor a legacy PHP app

The honest version of the rewrite question. When refactoring is a delusion, when rewriting is a delusion, and how to tell which one you're in.

May 3, 2026 9 min read

The rewrite quote always feels like clarity.

A new framework. A clean slate. A team that does not have to read someone else’s code from 2015. After a year of patching a system that nobody fully understands, the offer to start over reads like an answer.

It usually is not. But sometimes it is. The hard part is telling the difference, and the conversation rarely happens with the right framing.

What people mean when they say “rewrite”

“Rewrite” is almost never literal. It is shorthand for at least three different things, often blended together:

  • Re-architect. Same business logic, different system shape — break a monolith into services, swap a queue, replace ORM with raw SQL. The data model usually survives.
  • Re-platform. Same logic, different runtime. PHP to Node. Laravel to Django. WordPress to a headless CMS. The user-facing behavior is supposed to stay the same.
  • Re-product. A redesign of the actual offering — new features, different workflows, different pricing, different audience. The old system was a hint at the new one, not a blueprint.

These are not the same project. A re-architect can sometimes be done in place. A re-platform almost always blocks new features for months. A re-product is a startup inside a company that already has a system to maintain.

When someone quotes a rewrite, the first useful question is not the price. It is: which of those three are we actually doing?

Three things rewrites quietly assume

Most rewrites that go badly have the same three assumptions baked in. None of them are usually true.

1. The business logic is documented somewhere

It is not. The business logic lives in the code, the database, the support inbox, and a handful of habits the team has built around the system. The reason the legacy code looks ugly is that it accreted around real edge cases — refund flows, tax exceptions, partner integrations, the customer who has been on a grandfathered plan since 2018.

A rewrite that does not first extract this logic ships a clean version of a system that does not handle the cases that produce revenue. The first three months after launch are then spent reintroducing the exceptions, in worse code than the original, because nobody had time to write them well.

2. The team can hold two systems at once

For the duration of a rewrite, the team operates two systems: the old one, which keeps making money and accruing bugs, and the new one, which is not yet shippable. Every change in the old system is a change that has to be replicated in the new one — or a change the new one will silently fail to ship with.

Most teams underestimate how long this period lasts. They quote three months and live with two systems for eighteen. The result is a tax on every feature in both directions, paid by the same people who would otherwise be improving either one.

3. The rewrite ships before priorities change

This is the assumption that breaks the most rewrites. A rewrite scoped against today’s roadmap will not finish before next year’s roadmap is different. Funding round, market shift, founder change, cheaper competitor, new compliance regime — any of these reset the priority list. The rewrite, halfway done, is now solving a problem the business no longer has.

The system you actually ship is the system that reaches a usable state during a window where leadership still wants it. Rewrites have a much narrower window than refactors.

What a refit actually replaces

A refit is the alternative most teams have not seen described well. It is not “small refactors forever.” It is a sequence:

  • Map the system. Versions, dependencies, deploy path, backups, integrations. Make the unknown known.
  • Stabilize the runtime. Get off EOL PHP, EOL MySQL, EOL OS. Not because they are old, but because they block every other change.
  • Document the deploys. Move from “the developer who knows” to a path anyone can run.
  • Strangler-fig the bad parts. Replace the worst module behind the same interface. Then the next.
  • Decide. With a mapped, stabilized, documented system, the rewrite-or-not conversation becomes a real decision instead of a panic response.

Most legacy systems do not need a rewrite. They need the work above. After it, often, the rewrite case dissolves on its own — the parts that were unbearable were operational, not architectural.

When the case does not dissolve, you now have a stable platform to rewrite against, with documented behavior and a working rollback path. The rewrite that follows is meaningfully less risky than the one that would have been quoted at the start.

This is the shape underneath legacy PHP modernization and our audit — and it is why we always start with the audit, not the upgrade.

The honest decision rule

There is no algorithm. There is a question to answer truthfully.

Refit when:

  • The business model is the same one the system was built for
  • The team is small and cannot hold two systems for a year
  • Most of the pain is operational — fragile deploys, EOL runtime, missing tests, no documentation
  • The data model is mostly right, even if the code around it is not
  • Revenue depends on the system continuing to run during the work

Rewrite when:

  • The business has fundamentally shifted and the data model is now wrong, not just inconvenient
  • A regulatory or platform change has invalidated the architecture (a payment processor sunset, a host shutdown, a privacy regime)
  • The system is so coupled that no module can be replaced without replacing the whole thing — and you have eighteen clean months of runway to prove it
  • The team is large enough to support both systems through the transition

If you read those lists and most of the refit lines describe your situation, the rewrite is probably not the answer to the problem you actually have.

The middle path: strangler-fig refits

The cleanest case for partial rewrites is the seam. A legacy system often has one module that is genuinely beyond saving — the worst page, the slowest queue, the most coupled service. Rebuild that module behind the same interface, route traffic to it, retire the old code path.

You have rewritten one part of the system without taking on the cost of rewriting all of it. After the seam is replaced, decide on the next one. Often the team rebuilds three or four modules and never returns to the case for a full rewrite — because by then, the system is mostly the new code anyway.

This is what experienced teams mean when they say “we rewrote it.” They mean: over eighteen months, behind the same domain, no big-bang launch, no dual-system tax.

The signs you are reading the room wrong

Some signals that “rewrite” is solving the wrong problem:

  • The proposal arrived before anyone audited the current system. Diagnosis preceded examination.
  • The estimate is shorter than the time the existing system has been live.
  • The plan calls for “feature parity, then improvements.” Feature parity is the part most teams never reach.
  • The new stack is the one the team most wants to work in, not the one the business needs.
  • The cost of not rewriting is described in vibes, not in incidents.

If three of those are true, the rewrite quote is more about the team’s relationship to the codebase than about the codebase itself. That is a real problem — and it is not always solved by replacing the codebase. Sometimes it is solved by making the codebase trustworthy again, which is what a refit does. The refit-led case study at /sample-audit/ shows what that looks like as a written deliverable.

”Don’t rewrite, refit” is positioning, not dogma

We say it on the homepage and we mean it as a default. Most legacy systems, most of the time, should be refit before they are rewritten. The economics, the risk, and the timeline all favor it.

But it is a default, not a doctrine. Some systems should be rewritten. The way to know is not to argue about it abstractly — it is to do the work that surfaces the answer. Map the system. Stabilize the runtime. See what is left.

After that, the right question is rarely “rewrite or refactor.” The right question is “what is the smallest, most reversible thing I can ship next that makes the system safer?” Most of the time, the answer to that question is not a rewrite. The teams that learn to ask it stop needing the rewrite conversation at all.

If you want a structured way to answer it for a specific system, the audit is the deliverable that turns the question into a sequenced plan. If you have already seen the signs your PHP system needs a refit, this is the conversation that comes next.

Next step

Need this applied to a real system?

Request an audit →