Install, Update and Uninstall
Fresh installs, upgrades, skipped versions, reinstalls and downgrades. Learn what happens to user data at each step, how to test upgrades, and why every bug report needs a version and build number.
Monday, 8:05 a.m. A grocery delivery app ships version 3.0, a beautiful redesign. By 9:00 the support inbox is on fire: “I’ve been logged out”, “My saved shopping lists are gone!”, “Dark mode turned itself off”. The team had tested 3.0 for weeks, and every test passed. The catch? Every tester had installed 3.0 fresh. Nobody had started on 2.x, created real data, and then updated, which is exactly what millions of real users did that morning.
Most of your users will never do a fresh install of your newest version. They already have the app, with their account, favorites and settings, and they get the update in the background overnight. This lesson is about testing that journey.
Why it matters
- Data loss is the worst kind of bug. A crash annoys people. Losing their saved addresses, order history or workout streak makes them leave and write one-star reviews.
- It only shows up in one path. Upgrade bugs hide from testers who always start from a clean install. You have to deliberately walk the path from old version to new.
- You can’t easily undo it. Once a broken update is out, the store won’t let users go back to the old version. A hotfix helps the next users, not the ones whose data is already gone.
That’s why teams do upgrade testingUpgrade testingChecking that updating from an older app version keeps the user's data and settings working.Open in glossary → before every releaseReleaseMaking a version of the software available to users.Open in glossary →, and why you should know exactly what happens to an app’s data when it’s installed, updated or removed.
Four ways an app lands on a phone
| Scenario | What it means | What’s on the device before | Typical risk |
|---|---|---|---|
| Fresh install | The app was never on this device | Nothing | Onboarding, first permission prompts, empty states |
| Upgrade (update) | A newer version replaces the old one | All the old app data | Data migration, staying signed in, settings kept |
| Reinstall | Uninstall, then install again | Usually nothing, but some data can survive (see below) | Leftover login, restored backups, “ghost” data |
| Downgrade | An older version replaces a newer one | Data in a newer format | Old app can’t read new data: crashes, data loss |
The first launch
The very first launch after a fresh install is the user’s first impression, and it’s packed with one-time logic: onboarding screens, sign-up or sign-in, and permission prompts.
- Onboarding. A few welcome screens that explain the app. Check you can skip them, that they appear only once, and that they don’t come back after an update or a restart.
- Permission prompts. On iOS, the system asks for each permissionApp permissionsThe user's approval for an app to use things like the camera, location, contacts or notifications.Open in glossary → only once. If the user taps “Don’t Allow”, the app can’t ask again; the user has to go to Settings. So timing matters: a good app explains why first (“Get delivery updates?”) and asks at the moment the feature is needed, not as five pop-ups on launch. On Android 13+ notifications also need a runtime permission.
- Empty states. No orders, no favorites, no history yet. Every list should have a friendly empty state, not a blank screen or a spinner that never stops.
- Sign in, then everything else. Check the first sync after sign-in: favorites from another device appear, and the app doesn’t show another user’s cached data.
What happens during an update
When a user updates, the system replaces the app’s code but keeps its data: the database, saved files, preferences and Keychain/Keystore items. The new code then has to make sense of data written by the old code. That’s where things go wrong.
Data migration
If version 2.0 stores favorites in a new database table, it needs a migration: code that runs on first launch after the update and converts the old data into the new format. Things to check:
- Data is still there. Favorites, carts, drafts, history, downloaded files.
- The user stays signed in. If the new version stores the session token differently, the old tokenSession tokenA string the server issues after login that proves who you are on later requests.Open in glossary → must be moved over. Logging everyone out on update is a classic bug (and a support nightmare).
- Settings are preserved. Dark mode, language, notification preferences, units (km vs miles).
- Database schema changes work in every direction users can come from, which brings us to the next point.
Skipping versions
Not everyone updates every week. Some users go straight from 3.0 to 3.4, skipping three releases. If 3.2 added a migration and 3.4 assumes it already ran, those users break. Good apps run all missing migrations in order (3.0 → 3.1 → 3.2 → … → 3.4). Ask the developers which versions changed the data format, and test an upgrade from the oldest version still in use as well as from the previous one.
Forced updates (minimum version)
Sometimes an old version must stop working, for example because the server API changed. Apps then check a minimum supported version from the server and show an “Update required” screen. Test that:
- Versions below the minimum see the prompt and can’t continue.
- Versions equal to or above the minimum never see it. Watch out for
1.10vs1.9: compared as text,"1.10"comes before"1.9", so a lazy comparison thinks 1.10 is older. - The Update button opens the right store page.
- There’s also usually a softer “Update available” prompt that can be dismissed. It shouldn’t nag on every launch.
What happens on uninstall
Uninstalling deletes the app’s sandbox: its database, files, caches and preferences. But “deleted” isn’t the whole story:
| iOS | Android | |
|---|---|---|
| App files, database, preferences | Deleted | Deleted |
| Permissions | Reset (prompts show again) | Reset |
| Secure storage | Keychain items can survive a reinstall | Keystore keys are deleted |
| Cloud backup | iCloud backup restores data only when restoring a whole device | Auto Backup can restore app data (up to 25 MB) when reinstalling with the same Google account |
| “Keep data” option | Offload App removes the app but keeps documents and data | Some apps offer “Keep app data” when uninstalling |
On Android, the reverse surprise is Auto Backup: you uninstall, reinstall, and your old settings or even a stale login come back. That can be a feature (users love it) or a bug (restoring a token that the server already revoked). Know which one your team intends.
Try it: the version timeline
This is FreshCart, a grocery app with five releases. Play a real user’s journey:
- Install 1.0, go through onboarding, sign in, add a few favorites, and turn on Dark mode in Settings.
- Update to 1.1, then 1.9 (new database), then 2.0 (redesign). Check that everything survived.
- Reset, then do it again but skip straight from 1.0 to 2.0.
- Set the server’s minimum version to 1.9 and try different versions.
- Uninstall and reinstall. Watch the Keychain in the storage inspector.
- Try installing an older build over a newer one, and turn on Low storage before an update.
Check Settings › About for the version and build number, and keep an eye on the device log: it shows what the app does behind the scenes on every launch.
FreshCart is not installed
Pick a version on the timeline to install it.
Version timeline
- build 41
- build 48
- build 77
- build 83
- build 105
Nothing installed. Tap a version to install it.
Real stores only offer the newest version. Here you can pick any version to play users who update late, skip releases, or test older builds.
Storage inspector
App data
Deleted on uninstall
empty
Keychain
Can survive uninstall on iOS
empty
Permission
Notifications: not asked yet
Device log
- Install a version to start…
Low storage and app size
Phones are often almost full. Updates need space for the download and the unpacked app at the same time, so they can fail even when the app itself is small.
- The update fails cleanly. The old version stays installed and keeps working, with its data intact.
- The app copes when storage is full at runtime. Saving a photo, downloading a playlist or writing to the database can fail. The user should see a clear message, not a crash or silently lost data.
- App size is a feature. Check the size in the store listing and on the device (iOS: Settings › General › iPhone Storage › [app], Android: Settings › Apps › [app] › Storage). Watch for sudden jumps between versions, and for “Documents & Data” that keep growing forever (a cache that’s never cleared). iOS also asks for confirmation before downloading big apps over mobile data.
Store install vs test distribution
As a tester you’ll mostly install test builds, not store builds. They’re the same app, but not identical:
| Store build | Test build (TestFlight, Firebase, Play testing tracks, Xcode, adb) | |
|---|---|---|
| Who gets it | Everyone | Invited testers |
| Server | productionProductionThe live environment that real users use.Open in glossary → | Often a stagingStagingA pre-release environment that mirrors production as closely as possible.Open in glossary → or test environmentEnvironmentA setup of servers, data and configuration where the software runs, such as dev, staging or production.Open in glossary → |
| Can install older versions | No | Usually yes |
| Extras | None | Debug menus, logging, sometimes a different icon or name |
Before a release, make sure at least one upgrade test is done from the current store version to the release candidate, ideally on a real deviceReal deviceA physical phone or tablet used for testing, as opposed to an emulator or simulator.Open in glossary →. That’s the exact path your users will take. There’s more on beta channels in TestFlight and Firebase App Distribution.
Version and build numbers
Every app has two numbers, and you need both in every bug report.
- Major version
- Big, possibly breaking change (redesign, new database)
- Minor version
- New features, still compatible
- Patch version
- Bug fixes only
- Build number
- Goes up with every build the team makes, even when the version stays the same
iOS: CFBundleShortVersionString (version) + CFBundleVersion (build) · Android: versionName + versionCode
- Version (also “marketing version”) is what users see in the store, usually in semantic versioning style:
MAJOR.MINOR.PATCH.2.4.1means the second big generation, fourth feature release, first bug-fix release. - Build number identifies one specific buildBuildA specific compiled version of the app or website that is ready to be installed or tested.Open in glossary →. The team may produce ten builds of version
2.4.1while fixing bugs, and they all show2.4.1. Only the build number tells them apart. - On Android,
versionCodeis an integer that must increase with every Play Store upload. Android refuses to install a lowerversionCodeover a higher one, which is why downgrades need special tools.
Exact
Build: FreshCart 2.4.1 (318), staging · iPhone 15, iOS 18.5 · upgraded from 2.3.0 (290)
Vague
Build: latest
“Latest” changes every day. A developer reading your report next week can’t know which code you tested, whether the fix was already in it, or whether the bug only happens after an upgrade. See Writing bug reports for the full template.
What to test
Fresh install
- Onboarding appears once, can be skipped, and doesn’t return after restart.
- Permission prompts appear in context, and the app works when they’re denied.
- Empty states on every list; first sign-in syncs the right account’s data.
Upgrade
- From the previous version, from the current store version and from the oldest supported version (skipped releases).
- User stays signed in; favorites, carts, drafts, history and downloads are kept.
- Settings and preferences keep their values.
- Existing users don’t see first-launch onboarding again (a short “What’s new” is fine).
- Update while the app is running in the background, and with a pending action (an item in the cart, an unsent message).
Forced and optional updates
- Below / equal to / above the minimum version, including two-digit parts like
1.9vs1.10. - The Update button opens the right store page; the optional prompt can be dismissed and doesn’t nag.
Uninstall and reinstall
- After reinstall the app behaves like a fresh install: no previous user’s name, token or data (unless restoring is an intended feature).
- iOS: Keychain leftovers are cleaned up. Android: check what Auto Backup restores.
Edge cases
- Low storage during install and update; storage full while using the app.
- Downgrade with a test build (when the team supports it): clear error, no crash.
- Update interrupted (network lost, phone restarted).
Common bugs
| # | Bug | What you see | Typical severity |
|---|---|---|---|
| 1 | Logged out after update | Sign-in screen right after updating; the token was moved or renamed without migrating | High |
| 2 | Data lost on upgrade | Favorites, carts or history gone after updating | Critical |
| 3 | Migration only from the previous version | Works 3.3 → 3.4, loses data 3.0 → 3.4 | High |
| 4 | Settings reset | Dark mode, language or units back to default after update | Medium |
| 5 | Onboarding again | Existing users see welcome screens and permission explanations again | Medium |
| 6 | Forced update loop | “Update required” keeps showing after updating (string version compare) | Blocker |
| 7 | Crash on first launch after update | App crashes once, or every time, because of unexpected old data | Blocker |
| 8 | Ghost data after reinstall | Previous user’s name or token reappears after reinstall | High (privacy) |
| 9 | Update fails silently | Low storage: no message, app half-updated or old data broken | Medium |
| 10 | Cache that never shrinks | “Documents & Data” grows by hundreds of MB over weeks | Low–Medium |
Find the bug
This build of FreshCart has six upgrade and install bugs. Use the timeline like a real user would: create data on an old version, then update, skip, restart, uninstall and reinstall. Compare what the app shows with the storage inspector and the device log.
🐞 Bug hunt: FreshCart upgrade paths
This demo has 6 hidden bugs. Explore it like a tester, then check your findings.
FreshCart is not installed
Pick a version on the timeline to install it.
Version timeline
- build 41
- build 48
- build 77
- build 83
- build 105
Nothing installed. Tap a version to install it.
Real stores only offer the newest version. Here you can pick any version to play users who update late, skip releases, or test older builds.
Storage inspector
App data
Deleted on uninstall
empty
Keychain
Can survive uninstall on iOS
empty
Permission
Notifications: not asked yet
Device log
- Install a version to start…
- 1
Hint: Sign in on 1.0, then update to any newer version.
- 2
Hint: Turn on Dark mode and Weekly deals email on 1.0 or 1.1, then update to 1.9.
- 3
Hint: Add favorites on 1.0, then update straight to 2.0. Compare with 1.0 → 1.9 → 2.0.
- 4
Hint: Set the minimum version to 1.9, then update to the newest 1.x version.
- 5
Hint: Finish onboarding on 1.x, then update to 2.0.
- 6
Hint: Sign in, uninstall, then install again.
Tools and how-to
Upgrade test with TestFlight
- In the TestFlight app, open the app › Previous Builds and install the older build (or the current store version from the App Store).
- Use the app: sign in, create data, change settings.
- Install the new build from TestFlight without deleting the app. TestFlight installs it over the old one and keeps the data.
Simulator (from Xcode builds)
- Install a build:
xcrun simctl install booted OldFreshCart.app, then the new one the same way. Installing over an existing app keeps its data, just like an update. - Uninstall:
xcrun simctl uninstall booted com.example.freshcart. - Erase everything (including the Keychain): Device › Erase All Content and Settings in the Simulator menu.
Check storage: Settings › General › iPhone Storage › [app] shows App Size and Documents & Data, plus Offload App and Delete App.
Upgrade test with adb
adb install freshcart-1.0.apk, then use the app and create data.adb install -r freshcart-2.0.apk:-rreplaces the app and keeps its data, exactly like an update.- Downgrade (debuggable builds only):
adb install -r -d freshcart-1.0.apk.
Clean up
- Clear data without uninstalling:
adb shell pm clear com.example.freshcart(or Settings › Apps › [app] › Storage › Clear storage). - Uninstall:
adb uninstall com.example.freshcart.
Auto Backup: trigger a backup with adb shell bmgr backupnow com.example.freshcart, uninstall, and reinstall to see what gets restored.
Low storage: create an emulator in Android Studio’s Device Manager with small internal storage, or fill a test device with large files.
Example test cases
TC-UPG-001User stays signed in after updating from the store versionpositivehigh
Preconditions
- Current store version 2.3.0 (290) installed
- User is signed in
Steps
- Install release candidate 2.4.0 (318) over the existing app
- Open the app
Expected result
Home screen opens without a sign-in prompt and shows the same account.
TC-UPG-002Favorites and settings survive an upgrade that skips versionsedgehigh
Preconditions
- Version 2.0.0 (210) installed, user signed in
Steps
- Add 3 favorites
- Turn on Dark mode and set units to miles
- Update directly to 2.4.0 (318)
- Open Favorites and Settings
Expected result
All 3 favorites are listed; Dark mode is on and units are miles.
TC-UPG-003Existing users do not see onboarding after a major updatepositivemedium
Preconditions
- Version 1.10 installed and onboarding completed
Steps
- Update to 2.0
- Open the app
- Close and reopen the app
Expected result
No onboarding screens. A "What's new" sheet appears once and not after reopening.
TC-UPG-004Forced update compares two-digit version parts correctlyedgehigh
Preconditions
- Server minimum version set to 1.9
Steps
- Open version 1.1: note the result
- Open version 1.9: note the result
- Open version 1.10: note the result
Expected result
1.1 shows "Update required". 1.9 and 1.10 open normally with no prompt.
TC-UPG-005Reinstall behaves like a fresh installnegativehigh
Preconditions
- iOS device
- User "Anna" is signed in
Steps
- Delete the app
- Install it again
- Open it
Expected result
Onboarding and sign-in screen are shown. No name, data or token from the previous user is visible.
TC-UPG-006Update with low storage fails without breaking the old versionedgemedium
Preconditions
- Device has less free space than the update needs
- Old version installed with data
Steps
- Start the update from the store or TestFlight
- Open the app after the failure
Expected result
The system shows a storage message; the old version still opens with all data.
Tip: click a test case to expand it.
Checklist
Install, update and uninstall checklist
0 of 11 checked
Check your understanding
Quick quiz
0/5 answered · 0 correct
1.All your tests pass on a fresh install of 3.0. Why can users still lose data after updating?
2.A migration works from 3.3 to 3.4 but loses data from 3.0 to 3.4. What is the most likely cause?
3.Why does "Update required" keep appearing on version 1.10 when the minimum is 1.9?
4.On iOS, which data can survive uninstalling and reinstalling an app?
5.What should the "Build" field of a bug report contain?
Key takeaways
- Most users update, they don’t install fresh. Always test from an old version with real data to the new one.
- After an update, check login, data, settings and onboarding, and test skipped versions, not just the previous one.
- Forced updates must compare versions as numbers (
1.10is newer than1.9). - Uninstall deletes the app’s data, but iOS Keychain items can survive and Android Auto Backup can restore data. A reinstall should feel like a fresh install unless restoring is intended.
- Stores never downgrade; test builds can. Keep old builds around for upgrade testing.
- Every bug report needs the version and build number, the device and OS, and the upgrade path when it matters.
Finished this page?Nice work, this page is complete!
Mark it as done to track your progress. It's saved in this browser, no account needed.
Its review cards are now in Bites ⚡