projects

VeePeenini, Part 11: What Moving a Live App Taught Me

· Vitor Pontual · 4 min read

VeePeenini, Part 11: What Moving a Live App Taught Me

The move worked. The game runs from the second location now, every point and sticker accounted for. A few things stuck with me, including one I got plain lucky on and one that an old decision quietly handled for me.

The downtime was the feature, not the failure

The reflex with any cutover is to chase zero downtime. I went the other way on purpose. With two separate databases and no shared storage, that brief “front door down” window is the only moment I can prove both sides hold exactly the same data. Trying to eliminate it would have traded a visible ninety-second blip for an invisible risk of a lost trade. For a game whose entire value is that the standings are trustworthy, that is a terrible trade. Ugly and certain beat smooth and sorry.

Verify with fingerprints, not vibes

Matching row counts feel like proof and aren’t. Two tables can line up perfectly on count and still differ in the contents. The content checksums, an md5 over the actual points and stickers and trades, are the only reason I could tell my friends “nothing was lost” without crossing my fingers. If you ever move data people care about, fingerprint it on both ends and compare. Believing is not the same as checking.

The boring stuff is what bites

The near-miss in this whole project was never the database. It was a missing notification key sitting in a config file, and a background job that wrote to the database behind the app’s back. The headline task, move the app, was never the real risk. The five unglamorous details around it were. I keep relearning this: the scary-sounding step usually goes fine, and the thing that gets you is small, quiet, and off to the side.

An old decision paid interest

Back in Part 5 I had us derive the live-match clock purely from each match’s scheduled kickoff time instead of leaning on a live-data feed. At the time it was a simplicity call. During this move it paid off in a way I didn’t plan: the new box recomputes every match window on its own, from the same schedule, with no shared state to carry over. The move was effectively invisible to the game’s logic. Decisions about where your state lives keep paying or charging you interest long after you make them.

The timezone trap I didn’t fall into

The second machine sits in a different timezone from the home server. That could have shifted every scheduled job by hours, and for a game built around daily resets and kickoff times, that’s the kind of thing that breaks quietly. It didn’t, because the app runs on UTC internally and the daily reset is pinned in code to a specific zone rather than to wherever the server happens to sit. Another case of an earlier, slightly fussy decision turning into a non-event later. The one schedule that was tied to the machine’s local clock, a nightly health check, I caught and pinned down before the flip.

The honest part

I have not yet watched the new box grade a live match in real time, with everyone in the lounge. The cutover itself is verified down to the byte, and I’m confident in it, but a server settling a match live, in front of the group, is the test that actually counts, and it’s still ahead of me. So this is a win I’m fairly sure of, not one I’ve watched survive a full match day. I’ll know more after the next kickoff, and if something does go sideways, that’s a future entry.

The album was the fun part to build. Keeping it alive and intact through a house move, without anyone losing a thing, turned out to be the part I’m quietly proudest of. We’ll see how it holds up when the next whistle blows.

Update: it held

Then the next match kicked off, played out, and ended, and I watched to see what the new server would do on its own. The answer was: all of it. It caught the final whistle, graded every prediction against the odds it had frozen at kickoff, tallied the points, dropped the loot pack, and handed out a badge, with no errors and no nudge from me. That was the one test I admitted above I hadn’t run yet. It ran, and it passed. So the move isn’t just verified down to the byte anymore. I’ve now watched it survive a live match day, which is the part I was actually waiting on.