verbose log

Six Months Ahead: The Maintenance Story

12/10/2025 10 min

If you read the Wolf version of this post, you got the professional take: we bet on GraphQL and React Native Web in 2018, it paid off, the architecture held up. All true. But that’s the highlight reel. This is the part where I talk about what happens after that: what it actually felt like to maintain that code for six years.

We Made the Right Call

Let me get this out of the way first, because the rest of this post is going to sound like complaining: we made the right call. Every single decision we made was correct given the tools available at the time. We needed a unified codebase for web, iOS, and Android. Expo wasn’t ready for web yet. React Native Web existed but was barely proven — Twitter’s mobile web was basically the only production use case. We looked at the landscape, saw where the industry was headed, and built it ourselves.

No regrets. None. And it’s not just me being defensive about my own decisions. Engineers who’ve joined the team since — incredibly bright, talented people who came in with fresh eyes — have looked at our code, looked at when it was written, and agreed: we chose the right tools given what was available. Hindsight validates it. Retrospectives validate it.

Okay. Now let me tell you about the maintenance.

The Upgrade Treadmill We Couldn’t Stay On

Here’s the thing about being on Expo: when new versions of React and React Native come out, the Expo team does all the hard work. They figure out how to upgrade, test the crap out of it, and then hand you a detailed migration guide. You follow the steps, run a few commands, fix some deprecation warnings, and you’re done. It’s civilized.

We didn’t have that luxury.

Because Expo wasn’t ready for web when we started, we began with an Expo project, ejected it, and then pieced together our own structure. We were living outside the blessed path. When new React or React Native versions dropped, we’d wait for Expo to do their migration work, then try to use their guides as a blueprint for our own upgrades.

It worked. Sort of. To an extent.

The problem was that we had libraries and dependencies that Expo didn’t. We were assembling our own stack, and getting the exact combination of version numbers to play nice together was… let’s call it “non-trivial.” Every upgrade was a puzzle where half the pieces were documented and half were Stack Overflow threads that may or may not apply.

There was one upgrade path — I won’t bore you with the specifics beyond it was a major React revision — that proved so difficult we just gave up trying to do it incrementally. We started fresh with a brand new ejected Expo project and began porting our code and libraries over one by one. That effort took the better part of two months. Two months of “why won’t this compile” and “which version of this native module actually works with this version of React Native” and “oh, that library was abandoned, time to find an alternative.”

Two. Months.

The Technical Debt Compound Interest

Here’s where it gets rough.

We got so tied up in other work — adding features, expanding systems, building entirely new applications on the platform — that the technical debt of keeping the core libraries updated got completely set aside. There’s always something more urgent, right? A feature the business needs. A bug that’s affecting users. A new integration someone promised to a partner.

I would occasionally spend a month doing nothing but library updates, and those were all just the side libraries. Our core (React and React Native) kept falling further and further behind. The client application became basically fixed in time.

We talked for years about moving back to Expo proper. For years that project didn’t happen. There was always something else, always a more immediate priority, always a reason to push it to next quarter.

The Ticking Time Bomb

Here’s the reality we were living with: our client was a ticking time bomb.

Every year, Apple and Google make updates. New iOS versions, new Android versions, new requirements for what apps need to support to stay in the app stores. When you’re on current versions of React Native, you get those updates. When you’re stuck on an old version… you’re gambling.

We knew — I knew — that there would come a day when Apple or Google made an update that we simply could not support because we were locked into an old version of React Native. Maybe it would be a security requirement. Maybe a new API we’d need to implement. Maybe just a minimum SDK version bump that our ancient native code couldn’t handle.

The app would break, and we wouldn’t be able to fix it without a massive rewrite anyway. That was the future we were staring at.

Getting executive buy-in for “we need to rebuild the app because of technical debt” is… challenging. Execs don’t feel technical debt the way engineers do. It’s abstract. The app works, right? Users are using it, right? Why spend months rebuilding something that already exists?

What finally got us approval was putting it in terms they understood. Remember when Southwest Airlines had that catastrophic meltdown because their scheduling systems were running on legacy technology that couldn’t handle the load? Thousands of flights cancelled, passengers stranded, absolute chaos? That’s the analogy we used. “Our client app is Southwest’s scheduling system. It works today. It will not work forever. And when it breaks, it won’t be fixable — we’ll have to rebuild it anyway, except we’ll be doing it in a crisis instead of proactively.”

That landed. We finally got approval to take a small team and completely rebuild the client application in Expo.

The Rebuild: Doing It Right (Again)

The rebuild has been… honestly, kind of cathartic. We’re taking everything we learned from six years of running this platform in production and applying it with the benefit of hindsight.

Some systems we’re rebuilding exactly as they were — they worked great, no notes. Others we’re completely rethinking. When you’ve watched how users actually interact with your app for six years, you learn things. You see patterns you didn’t anticipate. You understand where your original assumptions were wrong.

And the best part? We’re building in vanilla Expo now. When new versions of React Native come out, we’ll follow Expo’s migration guide like normal humans. We’ll stay on the blessed path. We’ll let someone else solve the hard problems of keeping the native layer compatible. And we’ve already done this several times during the rebuild in Expo. It’s trivially easy now.

Looking Back, Looking Forward

I’m incredibly proud of what we built. We made a bet on technology that wasn’t proven, with a three-person team, under time pressure, for stakeholders who needed to see it to believe it. And it worked. The architecture we designed in 2018 is still running, still serving users, still handling business pivots we couldn’t have imagined.

The maintenance was hard. Harder than I expected when we started. But that’s the trade-off we accepted when we decided to build ahead of the curve. You can’t be six months ahead of the industry and also have the industry solve your problems for you. That’s not how it works.

And now, six years later, the industry has caught up. Expo supports web beautifully. React Native Web is mature and widely used. The future we bet on in 2018 is the present.

The new app is almost ready to ship. Same platform, same users, same business — but built on a foundation that someone else is maintaining. I can’t wait to see what we build when we’re not spending our energy just keeping the lights on.

That’s the real story behind “Six Months Ahead.” The wins were real. The pain was also real. And both of them were worth it.

Want the Wolf version? Head back to the original post.

root@wolf-solutions:~$ cd /blog/six-months-ahead