A Symfony upgrade that does not cost you a weekend

Most failed upgrades do not fail on the framework, but on what is missing around it. Our order of work, and why the first step has nothing to do with Symfony.

A Symfony upgrade that does not cost you a weekend

A screw jack carries the beam while the original post is lifted out from under it

Where upgrades actually fail

A Symfony upgrade that spirals rarely fails on Symfony. It fails because nobody could establish whether the application still did the same thing afterwards as before. Without that, an upgrade becomes a series of assumptions, and assumptions all come back at once on delivery day.

That is why the first step of our approach has nothing to do with Symfony.

Step one: make sure you can see the difference

Before a single version number goes up, we check whether there are enough automated tests to cover the core business processes. If there are not, we write those first — precisely where the money flows: ordering, paying, invoicing, signing in.

That feels like a delay and is the opposite. These tests are the only instrument that lets you establish afterwards that nothing changed quietly. They also outlive the upgrade, lowering the cost of every subsequent change.

Step two: stay within the major version first

Within a major version, Symfony warns about everything due to disappear in the next one. Those warnings are the map. So we first bring the minor versions up to date, collect every warning, and resolve them while the application keeps running. Only when that list is empty does the major version move.

Reversing that order — jumping straight to the new major — trades a list of concrete warnings for an application that no longer starts, with no indication of where to begin.

Step three: release in pieces, not in one night

An upgrade does not have to be a weekend operation. When the work sits in small, independently releasable steps, each step goes to production as soon as it is ready. If something breaks, you know immediately which step it was.

That does demand something of the surroundings: a repeatable deployment process and a test suite that runs on every step. At Newcraft that part was explicitly in scope — we updated the Symfony platform and its underlying infrastructure there and set up a new CI pipeline at the same time, so that developing and releasing ran smoothly afterwards. The pipeline was not a by-product; it was the precondition.

The three mistakes we see most

  • Skipping minor versions. That also skips the warnings that would have shown you the way.
  • Suppressing warnings. An ignored notice is deferred work with interest.
  • Upgrading and rebuilding at once. When something breaks, nobody knows whether the upgrade or the new functionality caused it. Do one thing at a time.

When upgrading is not the answer

Sometimes the honest outcome is that the upgrade does not solve the problem. If the application is built such that every change touches ten places, you will have the same application afterwards — only on a newer version. The gain then lies not in the version number but in untangling that, and that is a different conversation.

Also honest: on a neglected codebase, the cost of an upgrade cannot be stated exactly in advance. What can be done is a bounded assessment first, and a decision on that basis. That is considerably cheaper than a fixed price on an assumption.

Curious what is possible?

One conversation is usually enough to tell whether it fits. A reply within 24 hours, and no middlemen.

  • A reply within 24 hours, always personal
  • Half an hour on the phone gets you the order of magnitude
  • A first conversation without obligation
  • Including software originally built by someone else