Founder of Revin. Engineer by training, specialist in software development and digital products.

The upgrade ticket that never makes the sprint is the one that turns into a frozen quarter
If the change is a new version of something you already run, migrate a little every sprint and treat it as ordinary work. By version I mean the language runtime, the framework, the payment SDK, the container base image, the database going from 14 to 16. If the change is a different kind of thing, like another database engine, another cloud or another auth provider, that is a project. Even then it should ship in slices that run alongside the old system for weeks. The weekend cutover with everything frozen beforehand is usually the path you ended up on because you didn't pick the other one in time.
The two paths bill you in different places. Continuous migration takes a fixed, boring slice of every sprint, somewhere around 10 to 15% of the team, and nobody throws a party for it. The big switch takes a whole quarter of your roadmap. The timing gets picked by the vendor that ended support, the auditor or the incident. Rarely by you.
This came back up this week through an unexpected door. On his blog, Simon Willison answered a Lobsters thread asking which blog posts shaped people's thinking the most. One of his picks is Will Larson's 2018 post "Migrations: the sole scalable fix to tech debt". The idea Willison highlights is simple: replacing a service, switching database engines or moving to a new version is part of a system's normal life, not an accident along the way. Eight years on, a good share of the roadmaps I get to see still treat migration as the exception.

Three framework versions at once stack every breaking change from each release
Migrating a little every sprint means there is always an upgrade ticket on the board. A dependency that drifted two versions behind. The runtime going end of life next year. The base image piling up security alerts. None of those tickets ever makes it into a board deck, which is exactly why they are the first to get cut when the quarter gets tight.
The cost is predictable, and predictable is the best thing a cost can be. You know roughly 10 to 15% of capacity goes there, you know this week's feature ships a bit smaller, and you know the system stays one or two versions behind what everyone else runs. Each jump is small. The changelog fits in one read, and a failing test points to a single cause.
The less visible cost is discipline. Someone has to own that queue, hold the line when the ticket gets pushed for the third sprint in a row, and track how many versions behind you are. With no owner, continuous migration turns into continuous good intentions.
The big switch almost never starts as a decision. It starts as a notice. Your provider announces end of support for your database version. The auth library stops getting patches. A large customer sends a security questionnaire asking which versions you run, and the honest answer is embarrassing.
By then the accumulated gap has become a single leap. Three framework versions at once, with every breaking change from each one stacked together. A failing test now points to four tangled causes, and nobody can tell which came from which release. The estimate of about six weeks is optimistic from day one, because nobody can read three big changelogs and predict how they collide inside your codebase. I wrote about how that kind of number slides in the estimate that wins the deal is the one that slips the most.
And the roadmap stops. That quarter the team doesn't ship the feature sales already promised, and the conversation with the CEO turns into a negotiation over what gets cut. This is the point where tech debt stops being an engineering topic and becomes a revenue line: the delayed feature had customers waiting on it.

While old and new systems run side by side, every mismatch gets logged before the cutover
That is the honest objection, and anyone running product with three or four engineers knows it well. Ten percent feels like a luxury when the request list already overflows the sprint.
The answer is arithmetic. The slice exists either way, and the only choice is when you pay it. You pay in small installments, at a pace you set, or you pay all at once, with interest, on a date someone else set. A team that postpones upgrades for two years didn't save capacity during that time: it borrowed it, and the loan comes back as a frozen quarter.
It also helps to read the plan with less optimism. Plenty of good-looking schedules have no line for maintenance at all, and the deadline drifts quietly, sprint after sprint. I called this roadmap theater: the prettiest Gantt chart is often the one hiding the bill.
A client once asked me whether upgrading MySQL to a newer major version was the same thing as switching to Postgres. I answered with fruit. Two MySQL versions are a Gala and a Fuji. Postgres is an orange. They are all fruit, they are all SQL databases, and still you are comparing apples and oranges. Going from one apple to another changes size and taste, and the recipe stays the same. Going from apple to orange means rewriting the recipe.
The distinction sounds pedantic. It decides which of the two paths you are on. A new version of the same thing is sprint work: small, frequent, no ceremony. A different technology is a project, with a budget, an owner and an end date. The expensive mistake is treating one like the other. Treat the switch to a different engine as a routine upgrade and you find out halfway through that half your queries rely on features only the old database has. Treat a version upgrade as a special project and it sits waiting for a free quarter that never comes.
(The client laughed at the fruit. His question was a fair one, though. The product name on the invoice barely changes, and what changes underneath never shows up on a bill.)
There is a third case that trips people up: rewriting the product. Migrating swaps one part while everything else keeps running. Rewriting throws everything else away too. My reasons for almost never recommending that one are in why I almost never approve a full rewrite of your product.
Even moving to a different technology doesn't have to be a leap in the dark. The path with the fewest surprises looks similar almost every time: the new system runs alongside the old one, and traffic moves over gradually. In practice that breaks down into four moves.
The migration gets longer on the calendar and much shorter on surprises. It also demands something many systems don't have: knowing, with data, when something diverged or went down. If today your customer is the one who finds the problem, fix that before you start swapping parts. Migrating without instrumentation is changing the engine with the dashboard lights off.
Infrastructure moves carry one extra trap, and it is credentials. During coexistence you have two environments up, with two sets of keys, and the old one tends to stay reachable long after it was shut down on paper. The extreme version is the previous vendor who is still logged into your production.
Migrating a little every sprint assumes the system will keep existing. If the product is being sunset in six months, spending 10% of capacity on dependency upgrades is wasted time. Freeze it, isolate it from anything public-facing and let it retire quietly.
It also breaks when a dependency is abandoned outright. There is no next version to move to, so continuous upgrades have nowhere to go, and replacement is what's left. At that point you are back to the fruit question: are you swapping for something similar or for something else entirely?
And then there is the externally imposed date: a regulator, the end of a provider contract, a large customer demanding it. You can't slice a deadline someone else set. What you can do is start earlier, and that is the strongest argument for the fixed slice. Teams that were already one version behind barely notice the date arriving.
Before your next planning meeting, ask the team for a short table. For every dependency that matters (runtime, framework, database, base image, payment SDK), list the version you run, the current version and when support for yours ends. It takes an afternoon. When one of our squads takes over a system that came from another vendor, this table is among the first things it builds, before any feature, and it almost always turns up one or two red rows nobody had looked at.
Then one question just for you: how many rows in that table became tickets in the current sprint? If the answer is zero, you have already chosen the big switch. You just don't know yet which quarter it will land on.
8 read minutes
Article content: