#software-development
#founders
#product
Opinion

Incremental migration vs big-bang: what each one costs your roadmap

Upgrades of the same tech belong in every sprint. A different tech is a project, and even that ships in slices. What each path costs and where the rule breaks.

Por Victhor Araújo

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

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.

The fixed slice continuous migration takes

Three framework versions at once stack every breaking change from each release

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 quarter you don't get to choose

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.

"We don't even have room for the features we already promised"

While old and new systems run side by side, every mismatch gets logged before the cutover

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.

Apples and oranges, quite literally

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.

When you do have to switch fruit, the switch still ships in slices

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.

  • Write to both: every write goes to the old database and the new one, and the old one stays the source of truth.
  • Read from both and compare: a share of reads hits both systems, and every mismatch gets logged for someone to investigate before any cutover.
  • Cut over by slice: one customer segment, one region or one endpoint moves to the new system, and the rest stays put until that slice proves it holds.
  • Turn the old system off last, after a few weeks with no mismatches, with the date written into the plan so coexistence doesn't become permanent.

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.

Where this advice breaks

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.

The table to bring to your next planning session

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.

Ready to elevate your business

Schedule a meeting
Share
Link de compartilhamento LinkedinLink de compartilhamento XLink de compartilhamento WhatsappLink de compartilhamento Facebook

Every two weeks. The technical decisions we made, and what we learned.