How to Roll Out Breaking Changes Across a Portfolio
Rolling out breaking changes across a portfolio of products means expand, migrate, contract. Never break twenty apps at once. Here is the safe sequence I use.
Never make a breaking change to a shared foundation in one step. Split it into three: expand, migrate, contract. Add the new thing while the old thing still works. Move every product over one at a time. Only then remove the old thing. If you skip the middle and try to rename, remove, or reshape something in a single push, you are betting that all twenty products change in perfect sync, and they never do. The three-step sequence turns a scary all-at-once change into a series of small, reversible ones.
What is the safe way to make a breaking change across many apps?
The pattern is called expand and contract, sometimes parallel change, and it is the single most useful technique for a shared foundation. Here is the shape.
- Expand. Introduce the new interface alongside the old one. A new function, a new field, a new endpoint. Both work simultaneously. This step breaks nothing, so you can ship it immediately.
- Migrate. Move products from the old interface to the new one, one at a time. Each product changes on its own schedule, gets verified, and gets deployed. The blast radius of each step is exactly one product.
- Contract. Once every product uses the new interface, delete the old one. Now the change is complete and the codebase is clean, with no lingering compatibility cruft.
The magic is in the middle step. By decoupling "the new thing exists" from "everything uses it," you convert one high-risk event into many low-risk ones. This is the same discipline that lets you version a foundation without breaking apps, applied to the specific case where an additive change is not enough.
Why you never migrate all products at once
The temptation, especially for a solo operator, is to do a big-bang migration: change the foundation and fix all consumers in one heroic session. It feels efficient. It is not. It means every product is broken until the last one is fixed, and if something goes wrong in the middle you cannot ship any of them.
One product at a time keeps you always shippable. At every moment, either a product is on the old path and working, or on the new path and working. Nothing is ever in a half-broken limbo across the whole portfolio. If migration three of twenty reveals a problem in the new interface, you stop, fix the interface, and the other seventeen products are untouched and fine.
This is why I treat the portfolio's single points of failure so carefully. A breaking change rolled out badly is a self-inflicted single point of failure: you take down everything at once for no reason except impatience.
You cannot migrate what you cannot see
The whole sequence depends on knowing every place the old interface is used. In a portfolio of twenty products, that is not something you hold in your head. If you miss a call site, you contract too early and break a product in production.
So before I contract, I find every consumer of the thing I am about to delete. I use my build platform to search across all products at once, so Bootspring gives me the full list of call sites and I check them off as each migrates. Only when the list is empty do I remove the old code. Deleting on faith is how you turn a clean migration into an outage.
Good visibility also tells you when a migration has stalled. If three products still use the deprecated path a month after the deadline, that is a signal to go finish them, not to quietly extend the window forever. Half-finished migrations are their own kind of debt, and they are one of the mistakes that undermine a shared foundation over time.
Give the migration a deadline and own it
A breaking change without a deadline never finishes. The expand step ships, a few products migrate, and then the old and new paths coexist forever because there is no forcing function. Now you maintain two interfaces indefinitely, which is worse than either one alone.
Set a contract date when you start. Every product moves before that date. On that date, the old path dies. Treating the foundation like a real product with an owner is what makes the deadline stick, because someone is accountable for the migration actually completing.
Do this a few times and breaking changes stop being frightening. They become routine: expand, migrate on a schedule, contract on a date. The portfolio stays shippable the entire way through, and the foundation stays clean instead of accumulating a graveyard of half-removed interfaces.