Skip to content

Application modernisation

Laravel Upgrades & Rescue

Applications several versions behind, or inherited from a team that has gone - upgraded one version at a time, in production, with a test suite built before anything moves.

Two situations bring most teams here. Either an application has fallen far enough behind that upgrading feels like a project rather than a chore, or it was built by someone who has gone and the current team is afraid to deploy it.

Both are tractable. Neither usually requires a rewrite.

Upgrades, one version at a time

There is no shortcut across four major versions. The path is the versions in order, because each release's upgrade guide assumes you came from the one before it, and skipping means reasoning about interactions nobody has documented.

What makes it safe is the order of work:

  1. Coverage before movement. Characterisation tests over the routes and the jobs that matter - asserting what the application does today, right or wrong. Without this an upgrade is a series of changes with no way to tell whether they broke anything.
  2. PHP alongside Laravel. The two constraints move together, and the dependency graph usually decides the order. A package that has no release for your target PHP version is discovered now rather than three versions in.
  3. One version per branch, merged. Each step deployed on its own, so a regression is attributable to one change rather than to forty.
  4. Dependencies audited rather than bumped. An abandoned package is a decision - replace it, vendor it, or accept it - and that decision is cheaper made deliberately than made by a broken build at midnight.

The part that usually takes longest is not the framework. It is the packages around it, and specifically the ones that were abandoned somewhere between your version and the current one.

Inherited codebases

The first two weeks contain no commits. They produce a dependency map, an inventory of every route and job with what each one reaches into, a list of code that is provably dead, and a risk register ordered by what will hurt first.

You keep that document whichever way the decision goes. More than one client has taken it back to their own team and worked through it without us, which is a perfectly good outcome and one we would rather have than a reluctant engagement.

It is the risk register that changes how the conversation goes. Nobody can plan against "the codebase is a mess" - it is a mood. What can be planned against is: payments have no test, two jobs will double-charge if retried, queue failures alert nobody, and three packages carry published advisories with no upgrade path. That is a backlog, and a backlog gets worked in order by whoever has the week free.

What we find, nearly every time

Business rules in controllers. The same rule implemented three times slightly differently, so nobody can say what the application actually guarantees.

A .env that is the only documentation of the infrastructure. Services nobody can name, credentials nobody has rotated, and at least one value that is load-bearing and undocumented.

Jobs that are not safe to retry. Which has been fine because the queue has been quietly failing rather than retrying.

A staging environment that does not match production in the one dimension that matters - usually data volume, occasionally PHP version.

What holds the work together

Nothing moves without a test that would notice. That is the whole method. Coverage first, upgrade second, and where coverage is genuinely impractical, the change is smaller and the rollback is rehearsed.

Every step is deployable. No long-lived branch. If priorities change and the work stops for a quarter, what is merged is coherent and what remains is a list rather than a conflict.

The end state is written down. An upgrade with no recorded finishing line becomes, within one personnel change, a codebase where nobody knows which convention is current.

When an audit is the better first step

If what you have is a suspicion rather than a decision - the application feels fragile, or expensive to change, and nobody can say exactly why - an audit answers that in two weeks and tells you whether an upgrade is even the right response. Sometimes it is not: the version is fine and the problem is four endpoints and a queue that nobody watches, which is a far smaller piece of work.

How it is scoped

We read the application first: the version, the packages with no maintained equivalent, what the test coverage actually covers, and how far the framework has moved underneath it.

That produces a written scope with the version steps, their order, and a price. The estimate is per step rather than one figure for the whole journey, so you can stop after any of them with a working application and decide whether the next is worth it. The contract refers to that document.

Work starts once it is signed. One release at a time, each step its own pull request.

What you receive

The application on a current version, moved one release at a time, each step its own pull request so the change that broke something is the one you can point at. Alongside it the test coverage built to make the move safe, which stays afterwards and is usually the more valuable half.

You also get the list of what we deliberately left alone: packages with no maintained equivalent, code that works and should not be touched yet, and what each of those will cost when it is finally time.

Scope and terms

Engagement model
Fixed scope, agreed in writing before work starts. Not a day rate against an open backlog.
Price and timeline
Both are set per project, once the scope is. Quoted together, before anything is built.
What we need from you
One person who can approve decisions, and access to your repository and issue tracker.
Not included
Anything outside the agreed scope. It becomes its own scope rather than a variation order.
Third-party costs
Hosting, licences, API fees and SaaS subscriptions are contracted and paid by you.
Invoicing
Codefacture Yazılım A.Ş., Türkiye. EUR, USD or GBP by bank transfer, with no Turkish VAT on exported services.

Frequently asked questions

We are on Laravel 8. How bad is it?
Survivable, and more common than you would think. The work is mechanical rather than clever - one version at a time, with the PHP version moving alongside it. What decides the cost is not the version gap but how much test coverage exists at the start, which is why building that is the first phase rather than the last.
Can you upgrade without a feature freeze?
Usually yes. Upgrades happen on a branch that is rebased frequently rather than one that lives for months, and each version step is merged and deployed on its own. A six-month upgrade branch is how an upgrade turns into a rewrite nobody approved.
The original developers are gone and there are no tests.
That is the normal starting position for this work. We write characterisation tests first - tests that assert what the application currently does rather than what it should do - so that the upgrade has something to break. It is the least glamorous phase and the one that decides whether the rest is safe.
Is a rewrite ever the right answer?
Occasionally, and we will say so with a cost comparison rather than an opinion. But it is the answer far less often than it is proposed, and a rewrite of a system whose rules nobody has written down is the highest-risk project a business can run.
Call us+1 848 272 7583WhatsApp+90 850 308 5436Emailinfo@codefacture.comContact page