Vendor lock-in was never a technical property. It was a labour estimate.*

It was always a number in someone's head. The "engineer-weeks" to replan the topology, rewire the networking, redraw the bloody architecture diagram, re-derive the disaster recovery plan, and do it without dropping traffic. These facets were big enough to end the conversation. So you were immobilised by the inertia, and called it "a strategic decision" to keep the status quo.

Here's the argument before I've earned it: the primitives you had to work with never supported migrations the way swamp does. They were naive, fixed on the end state and nothing else. The sequencing, the glue, the verification, the disaster recovery: that is what a migration is actually made of, and none of it was ever modelled.


Four architectures, six weeks

What were we moving, so you can price the risk honestly: multiple storage planes. Demanding production traffic. Authentication and RBAC with real complexity behind it. Hundreds of deployments a week, throughout.

These were the architecture milestones:

  1. Railway. Two services, telemetry and swamp-club, running in parallel and privately networked, chatting to MongoDB running in Atlas.
  2. DO App Platform. Both services moved across. Railway couldn't give us the private networking our internal corporate apps needed, nor the firewall specifics around it. The outages were so brutal for us. At one point every running service on the platform went down because their upstream provider had blocked their account.
  3. DOKS (managed kubernetes). App Platform got expensive the way managed platforms always do: gradually, then all at once. Under $100 a month became about $500, with the curve still pointing the same way.
  4. ClickHouse on droplets (vms). The ClickHouse requirements grew: durability, off-site backups, and a lot more performance. In-cluster it was awkward and latency-ridden in a way that was never going to improve with affection, so it went onto its own droplets, privately networked back, with proper leader election.
The same two services, four platforms. The pressure continues to grow regardless.

I know how that reads. It isn't a lack of foresight. It's that our velocity is high enough that we cannot see that far ahead accurately. ClickHouse wasn't a requirement we failed to spot at the Railway stage. It became one. And forecasting a platform's requirements months out isn't only inaccurate, it's wasteful: you spend real time and real money building for a shape that never turns up. We only landed on the end state because of the iterations. Each move taught us what the next one had to be, and nobody could have drawn that final architecture at the start, because the information that produced it didn't exist yet.

What I've just described is almost just-in-time manufacturing, arriving seventy years late to application hosting. To recap, Toyota never got better at predicting demand. They got better at changeover, and once retooling took minutes instead of hours, small batches paid and the forecast stopped being load-bearing. The closer your architecture's velocity gets to the software's, the less you need to predict at all, because the answer to an unpredictable future was never better prediction. It was making the cost of being wrong small enough that prediction stops mattering.


What migrations actually cost

Not data volume or cutover windows. The expensive part is re-deriving what you already knew. Which data migration script was used last? When was that unusual environment variable added, and as part of which rollout? That lives in a few heads, a GitHub Actions log lost to data retention, and the scar tissue of the last three incidents. The brutal bit isn't the typing, it's that you don't have the data organised.

Paul Stack's framing is one worth returning to: when failure costs eight days rather than six months, the rearchitecture your team could never justify becomes "the work you do on a [mundane] Tuesday."

So lock-in was never the vendor's doing. It was the inertia caused by your inability to track and manage change across your own stack. I've argued before that the primitives can finally bend around the business rather than the business contorting itself around the primitives. A migration is where that stops being a nice idea and starts being the difference between moving and not.


Steady state is the wrong shape

There's a reason none of this was a Terraform job, and it isn't that Terraform is bad.

Desired-state tools assume you converge on somewhere and stay. A migration has no steady state. It's a sequence: stand the new thing up, dual-write, verify the shapes match, move traffic, reconcile what landed in the gap, and hold a branch for undoing all of it.

Worth being blunt about this: the two ends were never the hard bit, and never have been. Describing a cluster, a droplet, a bucket, that's the easy half, and Infrastructure as Code tools are genuinely good at it. Every bit of the difficulty lives in the glue and the sequencing between those states, which is precisely what no desired-state tool models. So you hand-roll it, and nothing tracks it. Idempotency, the property making those tools safe, then turns into the thing fighting you: half the steps are deliberately one-way. The core guarantee is the wrong guarantee for the job.

Instead, with swamp, every piece of every migration lands in a data lake. Including that sneaky little data-migration shell script your peer never shared. This also means move two was planned with move one's failures in front of it, and move four with all three. Migrations don't normally get cheaper with practice because the learning evaporates. When the record is a byproduct of the work rather than archaeology performed afterwards, they compound. That's the mandeep argument pointed at your infrastructure: knowledge recorded rather than recalled doesn't rot.


The half that never gets written

Every migration plan has a second half: what you do when it goes wrong. It decides whether the first half was a good idea, and most of it never makes it above the Minimum Viable Product line.

Two questions.

  • Rollback. You cut over at two in the afternoon, real customer writes land on the new system, and by the time you admit it isn't working, where do those writes go? That inflight data delta usually gets a shrug and a "let's just manually backfill after".
  • Verification. Not "the pods are green", but proof that what you moved does what it did before, at real volume.

Both are effort-hungry, so both get skipped.

Now both get planned up front, and the implementation gets asserted back against that plan. Which is why every architecture we moved to came out disaster-recovery validated from nil. A migration is already a DR drill: you stand the whole system up from scratch with the old one running beside it as a control. The only reason it doesn't count is that nobody keeps the evidence. Once the tooling helps you orchestrate and keeps the detailed records for you, not validating is laziness.

Was it 100% clean? Of course not. On the ClickHouse move, verification data left in the cluster polluted the post-migration numbers, so the comparison proving it had worked was contaminated by what it measured. Prewritten assertions caught it immediately, and the recovery was boring: re-run the data migration, idempotent by design, confirm zero delta. The sequence is one-way, but the data movement inside it needn't be.


Data inertia is real

The inertia of data and throughput is still real, and it still dictates the shape.

Moving a stateless service is close to free now. Moving state is not. Backfills take as long as they take, and nothing about agents makes a hot path tolerate a hop it didn't have before.

That's the more interesting half, because inertia and throughput are what steer the practitioner's hand. No amount of planning effort pulled ClickHouse out of that cluster. Analytical workloads have a throughput profile that doesn't want what a general-purpose cluster is optimised to give. That's physics choosing and us agreeing.

Strip out the "we can't justify the effort" tier and what's left is a smaller set of real constraints: where the data sits, how much there is, how fast it must move, what the hot path tolerates. They don't go away. They get louder, because they're finally some of the only loud voices in the room.


Lock-in is a choice now

You were never loyal, you were stuck. Providers know the difference, which is why the pricing curve bends upward once you're embedded. They were never charging you for compute, they were charging you for the fact that leaving would cost more.

That leverage was always proportional to how little of your system your tooling could actually hold. Give the primitives the whole job, sequencing and verification and disaster recovery included, and the leverage evaporates. Not because anyone got nicer, but because the number in your head got small.

Four architectures, six weeks, no drama. Not because we're heroes, and not because we saw any of it coming.

That's the project. Swamp is deterministic automation for AI agents: primitives that hold the whole migration, not just the two ends. Check it out at swamp-club.com.

Check your contracts. Then stay because the vendor is good, never because leaving is expensive.


* There are real exceptions, and they're worth naming: a commitment deal you signed to get the discount, or a partnership you can't walk away from. That's genuine lock-in. It's just legal rather than technical, and no tooling is going to save you from your own procurement department.