Building on shifting ground
Software engineering spent decades building a discipline around a single inconvenient fact: the things you depend on are maintained by other people, and other people change them. Every dependency a team takes on is a relationship with something it does not control, and the entire apparatus of modern practice — version numbers, lockfiles, changelogs, semantic versioning, deprecation windows, the ability to roll back — exists to manage that one relationship. The apparatus works because it rests on an assumption so basic nobody states it: a dependency holds still between the moments you choose to move it. You decide when to upgrade. Until you do, the thing you depend on is exactly what it was yesterday. The whole practice is a kit of tools for choosing when to absorb someone else’s change, and the choosing is the part that makes a dependency safe to build on.
The work a team produces now runs through a dependency that violates the assumption at its root. The model the team’s output increasingly passes through is, in every structural sense, a dependency: external, maintained by someone else, load-bearing under everything the team ships, and changing on a schedule the team does not set. But it is the first dependency a team has ever taken on that does not hold still. It changes underneath the team, on the maintainer’s timeline, with no version the team pinned, no changelog it can read, and no prior state it can return to. Every instrument the discipline built assumes a dependency you can freeze. This is one that cannot be frozen, and the team has no version of the practice that fits.
Three properties the discipline always relied on, each quietly absent here. The change announces itself — and this one doesn’t. The team picks the moment — and here it can’t. There is always a way back — and here there isn’t. None of the three failures shows up as a broken build. That is exactly why a team can be standing on shifting ground for a long time before it notices the ground was never the kind that holds.
1. The change with no signature
A dependency upgrade has always announced itself. The mechanism of the announcement is the interface: a function is renamed, a signature changes, a return type shifts, and the code that relied on the old shape stops compiling or fails the moment it loads. The break is loud by construction, because the contract a dependency exposes is its surface, and a surface either matches or it does not. Even semantic versioning — the convention that a major bump signals a breaking change — works because breaks live in the surface, where they can be enumerated and flagged in advance. The discipline learned to trust an inference it never had to defend: a change you weren’t warned about was a change that couldn’t hurt you, because anything that could hurt you would have broken the build first.
The model’s contract is not its surface. The thing a team depends on is not a function signature but a behavior — the character of the output the tool produces for the inputs the team sends it. And behavior has no signature to break. When the model changes, the call still exists, the call still returns, nothing fails to compile. What moves is the behavior itself: the same request that produced one kind of result last week produces a slightly different one this week — a little better on average, which is the entire reason the change was shipped, and different at the edges, where the team had quietly come to rely on the old behavior without ever writing the reliance down. The dependency changed and nothing broke, because the part that changed was never the part a toolchain watches.
This is behavioral drift. A team builds scaffolding around the tool — the instructions, the examples, the guardrails it tuned through trial until the output reliably landed where it wanted — and all of that scaffolding is fitted to a behavior that has since moved. The fit degrades by degrees. There is no stack trace for an output that is now marginally worse for a case the team forgot it was depending on; there is only a slow slide in quality with no event to attach it to. It is the inverse of the more familiar failure, where a team generates code and the code freezes while the world keeps moving around it. Here the team holds still and the dependency moves: the scaffolding is the frozen snapshot, and the tool beneath it is the shifting ground. Same widening gap between a fixed thing and a moving one — opposite which is which, and far harder to see, because nothing the team can point at has changed.
The remedy is to stop trusting the build to report that the dependency moved, and to construct the warning the toolchain will not provide. The contract migrated out of the interface and into the behavior, so the checks have to migrate with it: a held-out set of the cases the team actually relies on, exercised against the dependency itself, so that drift produces a red light somewhere deliberate instead of a degradation in production no one was watching for. A team cannot manage a change it cannot detect, and detection is now something it has to manufacture rather than something the compiler hands it for free.
2. The upgrade you didn’t choose
Even a breaking change, loud as it is, has always been a change the team chose to take. The instrument of the choice is the version pin. You read the changelog, you weigh the cost, you decide, you schedule the migration for a week the team can afford it — and until that week, you stay exactly where you are. The pin is the tool of refusal: it lets a team hold a known version indefinitely while it does everything else, and absorb the upgrade on a day of its own choosing. Control over timing was the whole point. A dependency you couldn’t pin would be one you couldn’t plan around, because someone else’s release schedule would become your roadmap.
The model upgrades on the maintainer’s schedule, not the team’s. The new behavior arrives because it is better, and “better” is delivered as a finished fact — there is no version the team asked to keep, no changelog enumerating what moved, no window in which the old and the new run side by side while the team migrates at its own pace. The improvement is non-negotiable and, from the team’s vantage, undated: it is simply the case that the tool is now slightly different than it was, with no event the team can name and no record of what the difference consists of.
This is the silent upgrade, and the asymmetry is worth stating plainly. A dependency maintainer who shipped a behavior change with no version bump and no changelog would be judged to have committed malpractice — the entire convention of versioning exists precisely to make that move illegitimate. For this dependency it is not negligence; it is the ordinary operating mode, and it is the ordinary mode because the change and the improvement are the same act. You cannot have the gains without the drift, because the gains are the drift: the vendor improving the tool and the vendor altering the behavior the team depends on are one event under two descriptions. The discipline assumed a team would initiate every dependency change it absorbed. Here the team initiates none of them. It is not the upgrader; it is the upgraded.
The remedy is not a pin the team cannot have. It is to treat the dependency as something that moves without notice and to take responsibility for the notice itself — to watch for the change rather than wait to be told, because no one is going to tell you. The planning the version pin used to enable has to be rebuilt around a release the team does not schedule and cannot postpone, which means the only real question left is whether the team learns of each shift from its own instruments or from an unexplained wobble in what it ships.
3. The version that’s already gone
The last instrument is the one a team reaches for when every other one has failed: rollback. When an upgrade breaks something the tests did not catch, the team reverts to the previous version and buys back the time to understand what happened. Rollback is the floor under the entire practice — the guarantee that no dependency change is ever truly irreversible, that there is always a known-good state to return to while the team makes sense of the new one. The pin lets you decline to move yet; rollback lets you un-move once you already have. Between them they ensure that a dependency change is never a one-way door.
That floor is mostly missing here. The previous behavior is not deprecated and then retired on a published timeline; it is simply gone, because there was never an artifact that was the previous behavior for the team to keep a copy of. Even where a version label can be held for a while, the thing the team actually depended on — the behavior — is not what the label durably guarantees, and the older label is itself retired on the maintainer’s clock rather than the team’s. The known-good state a team would roll back to does not exist as something it can hold in its own hands; it existed only as long as the maintainer kept it available, and the maintainer’s reason to keep it available expired the moment the better version shipped.
This is the retired version: the recourse every other dependency guarantees — return to the last state that worked while you figure out the new one — is the one recourse this dependency withholds. The team is denied the move the discipline was built to provide, the ability to refuse a change even temporarily, even just long enough to understand it. The dependency does not offer not yet, and it does not offer back. It offers only now, and again, differently, later, without telling you.
The remedy has to come from the one side of the relationship the team still controls. Since it cannot return to the old behavior, what it has to preserve is its own ability to function across whichever behavior it is handed — to build the scaffolding it owns to tolerate a moving dependency instead of a fixed one, to keep the surface it controls loose enough that a shift underneath it bends the output rather than shattering it. The discipline that can neither pin nor roll back has to be replaced by one that assumes movement and stays serviceable through it. Where a team used to buy safety by holding the dependency still, it now has to buy safety by making its own work robust to a dependency that won’t be held.
Closing
The dependency-management apparatus was built for artifacts that hold still between the moments a team chooses to move them. That was the unstated premise beneath version numbers, lockfiles, changelogs, deprecation windows, and rollback alike — not one of them survives the dependency moving on its own. And the most load-bearing dependency a team has now taken on moves on its own: it changes on a schedule the team does not set, in a way the build cannot detect, with no record of what shifted and no prior state to return to. Every tool the discipline offers assumes a freeze this dependency will not grant.
None of this is an argument against the dependency. The tool is load-bearing because it is good, and it is good in part because it keeps moving — a frozen version of it would be the worse deal, the way a library no one maintains is the worse deal. The error is not adopting it. The error is adopting it under the old assumption, wiring it under everything the team produces with the quiet expectation that it will behave tomorrow the way it behaved today, and discovering only later that none of the instruments which made every previous dependency safe to build on were ever designed for a dependency that does not hold still. A team can run for a long time on that expectation before the drift accumulates past the point of deniability, because nothing breaks along the way. That is the whole danger: the ground gives no sign, right up until the team’s output is visibly worse and there is no version, no changelog, and no rollback to explain why.
Every other dependency let a team answer a single question — not whether to absorb someone else’s change, but when. This one takes the question away. The ground a team builds on was always someone else’s to maintain; what is new is that it is now someone else’s to move, silently and without a way back, while the team is standing on it.