Skip to content
Ende Sun
Back to blog

Engineering

Migrating a production platform from Symfony 5.4 to Symfony 8

3 min read

Migrating a production platform from Symfony 5.4 to Symfony 8

Context

MasMusculo/MMfit is a gym-management platform built on Symfony, used every day by real gyms to manage memberships, bookings and billing. It was running on Symfony 5.4, a version that was heading towards end of support, on top of a codebase that had accumulated years of shortcuts typical of a product that grew faster than its tooling.

Jumping four major versions (5.4 → 6 → 7 → 8) on a codebase like that, as the only developer, with the platform live and in daily use, is a very different problem from migrating a small, well-tested side project.

The problem with skipping versions

The temptation with a legacy codebase is to look for a way to jump straight from 5.4 to 8 and be done with it. In practice that almost always backfires:

  • Each major Symfony version removes previously deprecated APIs. Skip a version and you lose the deprecation warnings that would have told you exactly what to fix before it became a hard break.
  • Third-party bundles rarely support more than one or two major versions ahead of what they were built for, so a big jump forces several bundle replacements at once instead of one at a time.
  • When something breaks after a four-version jump, there is no way to tell which of the four versions introduced the regression.

Strategy: one major version at a time

The approach that actually worked was boring on purpose:

  1. Upgrade to the next major version only, keeping everything else the same.
  2. Run the app locally and in staging, fix every deprecation warning Symfony itself reports, and only then move on.
  3. Run the regression tests that exist, plus a manual pass through the critical flows (sign-ups, bookings, payments) before each deploy.
  4. Deploy that single version bump to production, watch logs and error tracking for a few days, then start the next version.

It is slower than a single big jump, but every step is small enough to understand and revert if something goes wrong, and the platform never had to go down for it.

Lessons learned

  • Deprecation warnings are a to-do list, not noise. Treating every warning as something to fix before the next upgrade turned an otherwise risky migration into a predictable checklist.
  • Legacy code with thin test coverage needs a human safety net. Where automated tests did not exist yet, a short manual checklist of the platform's critical paths caught issues that would otherwise have reached real gyms.
  • Doing it as a single developer is a scheduling problem as much as a technical one. Spacing the four upgrades out over time, instead of trying to batch them, kept each change reviewable and the platform stable throughout.

The result: MasMusculo/MMfit now runs on Symfony 8, migrated without downtime and without feature regressions reported by the gyms using it daily.