"We installed the app — how do we get updates?" It's the question I get asked most often since I started doing self-hosted distribution, and the one that trips teams up most easily.

For a listed app, updates are something the store throws in for free. The user does nothing; Google Play and the App Store push the new version out in the background and even handle staged rollouts for you. The moment you go self-hosted (beta builds, in-house enterprise apps, utility apps), that pipeline is gone. The icon on the user's home screen stays frozen at whatever version they first downloaded, until they remember to re-download.

The consequences are heavier than you'd think. I once saw an internal tool sit un-updated for half a year; the API had changed its fields, old clients kept hitting the old fields, and it took two days to figure out "the users simply never got the new build." I've also seen a payment build ship a critical security fix that couldn't be pushed out — because there was no update channel to push through.

The core is two things: check the version, handle the gap

Checking isn't complicated. Each launch (or return to foreground), the app quietly fetches a version manifest you host, a trivial bit of JSON: {"versionCode":47,"versionName":"2.3.1","url":"https://onlymeteor.com/d/xxx","minSupport":40}. Compare the local versionCode against the remote one; if it's behind, there's an update. Use versionCode on Android (an integer, easy to compare) and CFBundleVersion on iOS. Don't compare versionName as a string — "2.10" being greater than "2.9" is exactly the kind of thing string comparison gets wrong.

What actually takes thought is how you handle the gap. That splits in two.

Optional updates: don't nag

One is the "optional update" — a new feature, a few bug fixes, take it or leave it. A small prompt with "Update now / Remind me later" does it. Just don't make the one mistake: remember the "later" the user tapped, and at least don't show it again that day. An app that prompts three times a day deserves to be uninstalled.

Forced updates: draw a disciplined line

The other is the "forced update" — a security hole, an incompatible API change, a version that will crash on launch. The user doesn't get to choose here; you have to block. My approach is a full-screen overlay with a single "Update now" exit and no "Skip" at all.

But the threshold has to be kept disciplined. Once you open this door, every product manager will believe their feature is "important," soon every release is forced, and users will eventually uninstall. My red line is three things only: it touches money, it touches login/auth, or the old version will crash — only those are forced; everything else is optional.

Forced update is a tool, not an excuse. Each time you use it, it loses a little force; by the time you genuinely need to block, nobody takes it seriously anymore.

A stable distribution entry point does half the work

If you're on the short-link-plus-landing-page setup, where the short link points at a package you control on the backend. "Updating" just means the user reopens the same link. You swap the backend pointer to the new build and the old link serves it automatically; the app doesn't even need to know a new URL. That's another reason I've basically stopped using raw download URLs: each raw URL is one more address to hardcode into the update logic, and that turns into a maintenance nightmare fast.

Is incremental updating worth it?

People always ask whether to do incremental (delta) updates. My advice: unless your package is absurdly large (games, tens or hundreds of MB), don't bother. Delta updates mean the server generates a per-device patch, the client integrates a bsdiff-style library, and you have to handle fallback-to-full on a failed patch. The bandwidth you save rarely covers the dev and debugging cost. For most apps, a few-dozen-MB full package downloaded over WiFi is already a fine experience.

At the end of the day, part of the 30% you save by self-distributing is exactly the bill for "updates." The store paid it for you, so you didn't see it; once you go solo, it becomes yours. Get version checking, the optional/forced split, and a stable distribution entry point solid, and don't over-engineer the rest. Users never wanted a fancy update animation — they wanted "tap once, and it's the latest."