Case study · Automotive B2B platform

By

A server move became a Laravel 8 to 13 modernization.

The client wanted to move a mature B2B application to a new server. I could have transferred it unchanged. It would have run on new infrastructure, but still on an old framework, with aging dependencies and little protection for its most important workflows.

I proposed a controlled upgrade rather than a rewrite. The goal was to make the application maintainable again without reinventing ordering, stock, logistics, documents, or billing along the way.

The B2B application moving from Laravel 8 to Laravel 13
The real assignment was larger than the planned server move. The system needed a current and verifiable technical base.

Starting point

This was not an ordinary server move

The platform handles ordering, stock movements, logistics, documents, and billing. It also exchanges data with external systems. A defect does not merely affect an old administration tool. It can stop real work or produce incorrect business data.

During the assessment I found Laravel 8, PHP 8.0, Livewire 2, Laravel Mix, and several dependencies that blocked newer framework releases. The application also lacked enough automated protection for a responsible multi-version upgrade.

That changed the assignment. The server still had to move, but the move became the final step of a larger modernization.

The decision

Why I modernized the application instead of rewriting it

A rewrite would have looked cleaner. It would also have turned years of accumulated rules back into assumptions. Which order lines may be combined? When can an invoice be sent again? Which permission applies if access is revoked between two requests?

Rules like these rarely exist in one complete specification. They live in code, database structures, and the expectations of the people who use the system every day.

So I kept the business core. I replaced the technical foundation in controlled steps and changed domain behavior only when tests exposed a defect or an unsafe boundary.

The approach

First, the old application had to explain its behavior

Before updating Composer, I worked through routes, Livewire components, jobs, migrations, and the most important workflows in the running application. That investigation became tests, factories, and support code around its existing business rules.

This first phase added 10,423 lines. The number itself is not the point. What mattered was the behavior those tests captured: orders, permissions, cart behavior, documents, background jobs, and external handoffs.

The work also found defects that already existed on Laravel 8. I fixed them before upgrading, so a later failure would not leave me guessing whether the framework change had caused it.

Delivery

Five Laravel versions without one giant leap

I moved from Laravel 8 through each major release to Laravel 13. PHP tests, the frontend build, and browser flows had to work again after every stage. Only then did I move to the next version.

That may sound slower than one large jump. In practice, it cuts investigation time. When something breaks, the cause sits in a small change set instead of somewhere across five framework releases and a completely different frontend build.

The modernization remained understandable across 35 focused commits. Each step had one clear job and a result I could verify.

AreaBeforeAfter
BackendLaravel 8 · PHP 8.0Laravel 13 · PHP 8.3
UILivewire 2 · Tailwind 2Livewire 4 · Tailwind 4
Build / TestsMix · PHPUnit 9Vite 8 · PHPUnit 12

The difficult parts

The real work happened between framework upgrades

Laravel itself was rarely the largest problem. The harder work sat where the old system carried assumptions nobody had written down.

An abandoned cart package blocked the upgrade

I neither copied the entire package nor rewrote checkout at the same time. The application took ownership of the small contract it actually needed, including cart instances, items, options, events, totals, and stored carts. Existing tests preserved its behavior.

Livewire turned browser state into a security concern

I treated public properties as request data. The server recalculated prices, checked record ownership again during saves, and denied access that had been revoked between requests. Render tests would not catch these failures, so the suite exercised complete actions.

Background jobs had to survive repetition

A repeated request could not create a second order. A worker restart could not deliver the same document or email twice. Persistent status records, unique jobs, and locks made those operations safe to retry.

Passing PHP tests do not prove the production build works

During the move from Mix to Vite and Tailwind 2 to 4, dynamically assembled classes could disappear from the final CSS. A build check now looks for 22 required selectors in the generated file. I also replaced 124 external icon-component usages with local SVGs and removed 17 Composer packages.

Going live

The code was not done until it ran safely in production

The upgraded application first ran in isolation on the new runtime stack. Anonymized data made real business workflows testable. Mailpit captured outgoing messages so a test run could not reach a customer.

Before the switch, I checked the scheduler, queue, public entry points, six browser flows, Blade compilation, Composer audit, and the production build. Client acceptance was followed by a data freeze, final database migration, and domain switch.

On the new host, I checked authentication, ordering, documents, email handoffs, the queue, and the scheduler again. Only then did I consider the modernization complete.

The result

What the client received

The client did not receive a new application that had to relearn how the business works. The familiar workflows now run on a current technical foundation, with much stronger protection for future changes.

Laravel 8 → 13
Moved to a supported framework release
698 PHP tests
With 4,593 assertions around business behavior
6 browser flows
Checked against the real production build
17 packages removed
Fewer dependencies in the running application

The application now runs on Laravel 13 on the new host. Its tests document more than whether pages render. They preserve the business workflows that the next change must not break.

Looking back

What I took away from this modernization

I would again start with behavior, not the target version. Laravel has a documented upgrade path. The harder job is understanding a mature application well enough to know what may change and what must remain untouched.

I would also move through one major release at a time again. Large jumps look shorter in a project plan. Small steps that are fully checked are usually faster in a business-critical application because failures have a clear origin.

And I would again treat the production switch as part of development. A modernization does not end with green CI. It ends when people, jobs, and integrations continue to work reliably on the new system.

My role

I planned, delivered, and took the modernization into production

I am Mathias Onea, a software engineer focused on Laravel, PHP, and mature business applications. On this project I was responsible for the assessment, upgrade strategy, tests, security fixes, frontend migration, and production switch.

The figures on this page were checked against the final upgrade branch and its successful CI run.

FAQ

Questions about Laravel modernization

When is a Laravel upgrade better than a rewrite?

When the application still works for the business and contains years of accumulated domain logic. An incremental upgrade preserves that value, while a rewrite has to prove every detail again.

Do you need to upgrade through every Laravel major version?

For a large version gap, this is usually the easier path to verify. After each stage, failures belong to a small change set that can be understood and fixed.

How can you reduce the risk of a Laravel modernization?

Test real business workflows, keep upgrade steps small, use a production-like staging environment, and plan the cutover with explicit checks before and after the switch.

Can product development continue during the modernization?

Yes, if upgrade work and ongoing product changes are separated and merged regularly. The useful level of parallel work depends on test coverage, team size, and the rate of change.

Facing a similar application?

Let us first work out what must be preserved.

For an initial assessment, I need the current Laravel version, the most important business workflows, and an overview of tests and deployment.

Discuss a modernization