So I’ve spent months doing archaeology. I’ve mapped the dependency graphs. I’ve identified why the migration stalled. I’ve proposed a plan.

Now comes the hard part: getting dozens of engineers to actually adopt it.

Oh, and did I mention this is all happening during COVID lockdowns? While food delivery demand is exploding? While product managers are setting aggressive timelines? While engineers are scrambling to hit weekly build cuts?

Yeah. Welcome to the politics of platform engineering.

This is fine

The Problem With Having a Plan

I presented the architectural plan. Got buy-in from the Senior Director. Engineers agreed it was a good idea. All the checkboxes you’d want to tick, ticked.

And then nothing happened.

Engineers had features to ship, right now, during a pandemic. Feature velocity became the only priority anyone cared about. “We’ll migrate later when we have time” became the refrain, and later never came.

The problem wasn’t technical. Every engineer I needed to convince had their own stakeholders. Product managers with roadmaps. Tech leads with quarterly goals. Directors measuring feature delivery. Users who needed the app to work. None of those stakeholders cared about Swift.

The Constraint That Changed Everything

The Senior Director gave us one non-negotiable constraint: “You cannot rewrite the app.”

This sounds obvious, but it shaped everything. We couldn’t create a parallel “Swift version” of the app, or pause feature development for a big rewrite. We couldn’t break active development happening in parallel, and we couldn’t force teams to stop what they were doing and migrate.

We had to migrate in place. Incrementally. While dozens of engineers kept shipping features. And hit the weekly build cut. And not break production.

Oh, and do it during a global pandemic while food delivery demand was exploding.

No pressure.

Stressed out

The COVID Context

Lockdowns. Food delivery goes from “nice to have” to “essential service.” Usage spikes. Teams are underwater.

Product managers: “We need these features yesterday.”

Engineers: “We’re already working weekends to hit the build cut.”

Me: “Hey, want to refactor your module’s dependency graph?”

You can imagine how well that went.

I wasn’t just competing with feature work. I was competing with crisis mode. Every hour an engineer spent on migration was an hour they weren’t shipping features that users desperately needed.

The math was brutal. A feature could ship in days and impact millions of users. Migration work might take weeks and benefit… future engineers? The mythical “after we migrate everything” state?

Features won every time.

The Memory of Past Pain

But even before COVID, there was resistance. And it had a name: Swift 3.

The Swift 3 migration had happened a few years earlier. It broke everything. Thousands of compiler errors. Weeks of work to fix. Code that worked perfectly fine suddenly needed rewrites because the language changed.

Engineers remembered the pain. The lost weekends. The features that slipped. The incidents from rushed fixes.

So when I showed up saying “we should migrate to Swift,” the reaction was… skepticism.

“Didn’t we already try this?” “Is this going to be another Swift 3 situation?” “What happens when Swift 6 comes out and breaks everything again?”

And honestly? Valid questions. I couldn’t promise Swift wouldn’t change again. I couldn’t promise the migration would be painless. All I could promise was that not migrating meant staying stuck forever.

That’s a hard sell when people are busy.

The Incident Tax

Incidents were expensive, and that made everything harder.

We had aggressive feature flagging for new features. But legacy Objective-C code? Much of it couldn’t be easily feature flagged. Patterns from 2014 didn’t have clean on/off switches.

Which meant: if you broke something during migration, it could ship to production. And production incidents were costly. Eng hours to fix. User impact. Postmortems. The scrutiny.

So engineers were risk-averse. Understandably.

Every engineer ran the same calculation. They could write a new feature in Swift using existing interop layers: safe, familiar, ships fast. Or they could migrate core Objective-C code: risky, time-consuming, might cause incidents. The safe path won every time.

The interop layers the mobile platform team had created solved the immediate problem (write new code in Swift) but prevented the actual migration (convert old code to Swift).

We’d created a comfortable middle ground that let everyone avoid the hard problem.

Avoiding the problem

The Multi-Level Buy-In Game

I had buy-in from the Senior Director. That didn’t mean I had buy-in from anyone else.

Engineers saw extra work. Tech leads supported it in principle, as long as their teams didn’t take on more than they already had. Product managers wanted to know if it would delay their roadmap. Directors needed to see how it impacted quarterly metrics.

Each level had different concerns, different incentives, different definitions of success. The Senior Director cared about technical debt and long-term maintainability. Directors cared about quarterly delivery and team velocity. Product managers cared about shipping features. Tech leads cared about not burning out their teams. Engineers cared about not working weekends.

All valid concerns. All in tension with each other.

I needed to convince all of them simultaneously, with different arguments for each level. Having a plan isn’t enough. You need a strategy for adoption.

The Objective-C Loyalists

And then there were the Objective-C loyalists.

Yes, they existed. Engineers who genuinely preferred Objective-C over Swift. Who saw the dynamic runtime features as advantages, not liabilities. Who found Swift’s strictness annoying rather than helpful.

They weren’t wrong, exactly. Objective-C has real strengths. The runtime is powerful. Interop with C and C++ is seamless. The language is stable (because it’s finished evolving).

Swift? Still changing. Stricter. Breaking changes between versions. Less mature tooling in some areas.

So when I pitched “migrate to Swift,” these engineers heard “throw away a perfectly good tool for a newer, buggier one.”

I couldn’t change their minds by arguing Swift was objectively better. I had to show that the migration was inevitable and help them see the path forward.

That’s a very different conversation than “Swift is better, let’s migrate.”

Different perspectives

The Weekly Build Cut

Every week, there was a build cut. A deadline. Code needed to be merged, tested, and ready to ship.

This weekly rhythm created constant pressure. Engineers were always working toward the next cut. Always trying to land features. Always fixing last-minute issues.

In that environment, migration work was a luxury. Something you’d do “after the build cut” or “when things calm down.”

Things never calmed down.

The weekly cycle reinforced feature work over migration work. Features had deadlines. Migration didn’t. And in a world of competing priorities, deadlines win.

The Strategy That Actually Worked

So how do you get adoption in this environment?

You don’t mandate it. You don’t create a top-down edict. You don’t say “everyone must migrate by Q3.”

You lead by example. You make migration the path of least resistance.

The mobile platform team took ownership of the Core modules. We didn’t ask feature teams to do the hard work. We did it ourselves. We migrated the Core modules that everything depended on, built the bridging layers, and created the patterns. Feature teams could keep shipping. We’d handle the foundation.

Every migration we did, we documented. Tips, tricks, patterns that worked, pitfalls to avoid, examples of successful migrations. We didn’t hoard knowledge. We showed our work, made it visible, made it easy for others to copy.

When teams had questions, we helped. When they hit blockers, we unblocked them. When they wanted to migrate, we enabled them. But we never said “you must migrate now.” We said “when you’re ready, here’s how.”

We improved tooling too. Built linters that caught common issues. Created automated migration scripts. Reduced friction. Eventually, migrating became easier than fighting with the old architecture. When that happened, teams started choosing to migrate on their own. Not because we told them to. Because it made their lives easier.

And when teams did migrate, we highlighted them. Shared their success. Made them heroes. This created social proof. Momentum built slowly. But it built.

Building momentum

The Turning Point

The turning point came months later. A feature team hit a nasty bug in legacy Objective-C code. They spent days debugging it. Finally realized: the bug was in a pattern that the migrated Swift version had already fixed.

The tech lead said: “Screw it, we’re migrating to the new architecture.”

They did. It took a week. But after that week, their velocity actually improved. Fewer bugs. Cleaner code. Better tooling support.

Other teams noticed. “Wait, migration makes things faster?” Word spread. More teams started migrating. Not because we mandated it. Because they saw the value.

You can’t force adoption in platform engineering. You can only create the conditions where adoption becomes inevitable.

What I Learned

Executive buy-in is necessary but not sufficient. You need buy-in at every level, and each level responds to different arguments.

Crisis mode kills long-term work. When everything is urgent, nothing that’s “just important” gets done. You have to find ways to make long-term work relevant to short-term goals.

Past pain creates present resistance. The Swift 3 migration left scars. You can’t ignore that history. You have to acknowledge it and show how this time is different.

Risk aversion is rational. When incidents are costly and migration is risky, people will avoid migration. Reducing the risk matters more than telling people to be brave.

Competing incentives are the real blocker. Engineers want one thing. Product managers want another. Tech leads want a third. Your job is to align incentives, not override them.

And above all: do the hard work yourself. Show it’s possible. Make it visible. Make the new way easier than the old way. Don’t appeal to principles. Appeal to pragmatism. When migration is easier than staying put, people migrate.

What’s Next

So here I am, months into convincing dozens of engineers across many teams that migration is worth doing.

We’ve migrated some of the Core modules. We’ve created patterns. We’ve shared examples. Early adopters are starting to migrate.

But we’re still in that uncomfortable middle state. Half the app is Swift. Half is Objective-C. And every change we make has to work in both worlds.

Now comes the fun part: actually doing the migration at scale. While keeping the app stable. While teams keep shipping features. While not breaking production.1


Footnotes

  1. Narrator: Something was definitely going to break. Stay tuned for Part 3. ↩