Where this comes from
From August 2024 to August 2025 I worked as a Software Development Engineer in Test for TAKADOM AL-IRAQ (TTech), on a freelance basis. I was responsible for mobile, web and API testing on public-service products, and I built the Kotlin and Selenium automation for the Naftcom project, with API and regression coverage around its citizen and station workflows. That code is in a private repository, so I will not describe it in detail here.
I also build mobile apps. At Apps Square I am responsible for the mobile apps of several products, from setup and architecture through testing and delivery. At Inside Tech I built Bismar's customer and seller apps on Kotlin Multiplatform as the only mobile engineer on them, and both have public App Store and Google Play listings.
The testing habit comes with me. When I read a feature now, my first question is still what happens when this fails.
Test the thing you ship
A debug build proves very little about a release. The checklist is explicit that I test the signed release build with production settings, because code shrinking and release configuration can break things a debug build never shows.
A few items sit next to that one because they fail in the same quiet way:
- Version and build number. Every upload needs a higher build number than the last (
versionCodeon Android, the build number on iOS), and the visible version must match the release notes. - Signing keys. Use Play App Signing so a lost upload key can be reset. Back up keys and passwords outside the repository.
- Crash symbols. Upload the R8/ProGuard mapping file and the iOS dSYMs to the crash reporter. Without them, production stack traces are unreadable and the first real crash is a guessing game.
- No debug leftovers. Check base URLs, feature flags, verbose logging of personal data and hidden test menus in the release configuration.
Devices and the upgrade path
The checklist asks for real devices at both ends: at least the oldest OS version you support and the newest, a small screen and a large one.
The upgrade test is the one I would least like to skip. Install the version on the store today, create some data, then update. Sessions, saved settings and local data must survive. A fresh install can be perfect while the person who has used the app for a year loses their session.
Failure paths first
This is where the QA background shows most. The checklist's network item reads: test slow, offline and flaky connections, confirm loading, retry and error states work, and make sure a repeated request never creates a duplicate order or payment.
That last clause is the important one. A retry that works is only half of the feature. The other half is that the retry is safe.
Here is a small example from a Flutter app I worked on, Fueler. The home screen loaded account, wallet and product data in parallel, and also resolved the user's location to find nearby stations. The original code waited for all of it before showing the page, so a slow location fix hid the wallet balance behind a loading state, even though the wallet never needed coordinates.
The test that guards the fix does what a tester would do: it delays one dependency on purpose.
// Simplified from the project's test file.
locationResolver.delay = const Duration(seconds: 3); // a genuinely cold fix
final loading = cubit.load();
await Future<void>.delayed(const Duration(milliseconds: 400));
// The screen is already usable before the location fix lands:
expect(cubit.state.status, HomeStatus.loaded);
expect(cubit.state.profile, _profile);
expect(cubit.state.products, _products);
// ...and the sections that need coordinates say so honestly:
expect(cubit.state.locationAccess, LocationAccess.unknown);
expect(cubit.state.stations, isEmpty);A second test in the same group asserts that a warm fix emits exactly one state per refresh, so the stations section does not flash back to "locating". I did not measure device timing or any conversion effect, and I am not claiming one. The point is the shape of the test: slow down one thing, and check what the rest of the screen is allowed to do.
Permissions and the language you read in
Camera, location and notifications each need a test for the case people forget: the person taps Don't Allow. The app should keep working and explain why it needs the permission.
The checklist also treats Arabic as a test configuration, not a translation task: mirrored layouts, mixed Arabic and English text, and the system font size at its largest. Right-to-left bugs are easy to miss when you read the interface in only one language.
The API is part of the release
A mobile release is half a backend release, and the half people forget is the old app. People update slowly, so the checklist asks that the API keep serving the installed version: old endpoints and response shapes keep working, or a forced-update path ships first.
A minimum-supported-version check lets you retire a broken build without waiting for everyone to update. And on the data side: run migrations on a copy of production data first, and take a backup before the release.
I carry the same habit into my own tooling. PostSync Pro, an open-source Compose Desktop toolkit I built, sends REST requests, runs bounded assertions and validates responses against an OpenAPI specification. It is published as a beta, and the screens that still use simulated data are labelled as previews.
A regression pass on what hurts most
I do not try to retest everything. The checklist says to run a regression pass on the flows that matter most: sign-up, sign-in, checkout or booking, and anything a support call would be about. Notifications and deep links get their own check: they should open the right screen from a closed app, from the background and when signed out.
Store rules are test cases too
Both stores publish rules that read like requirements, so they become checks. A few from the checklist:
- App Store: build with the required SDK (iOS 26 SDK or later, since 28 April 2026); keep App Privacy details in line with what the app and its SDKs collect; give App Review a working demo account and keep the backend on during review; offer account deletion inside the app; if you offer a third-party sign-in, offer an equivalent option such as Sign in with Apple; answer the encryption question in
Info.plist. - Google Play: keep the Data safety form in line with the app; meet the target API requirement; provide sign-in details for review that keep working (reusable, no one-time code, not expiring); and for a new personal developer account, run the closed test before applying for production.
Dates and thresholds change, so the checklist links each rule to the official page and records when it was last checked. Re-check before you rely on any of it.
A store review is a separate gate with its own rules. It does not tell me whether checkout survives a flaky connection, so it never replaces my own testing.
Evidence, not memory
After a release I want to answer what was tested, on what, and what happened. A short record is enough. This is an illustrative template, not a file from a specific project:
release: 2.4.0 (build 118)
artifact: signed release build, production config
devices: [oldest supported OS, newest OS, small screen, large screen]
upgrade_from_live: passed, session and local data kept
network: slow / offline / flaky checked, repeated request created no duplicate
permissions: camera, location, notifications denied once each
arabic_rtl_large_text: checked
regression: sign-in, checkout, support-call flows
backend: old endpoints still served, min-version check in place
symbols: mapping file and dSYMs uploaded
rollout: staged, halt criteria agreed
owner_first_72h: namedWriting it down changes behaviour: a skipped line is visible, and "I thought we checked that" stops being an answer.
Launch day and after
The release is not over when the build is live. The last section of the checklist asks for:
- release notes written for people, in every store language, saying what changed and whether they need to do anything;
- someone who watches the first 72 hours, with an owner for crash reports, store reviews and support messages;
- a plan for a bad release, since an installed update stays until you ship a fix: keep a hotfix path and server-side switches ready;
- release analytics verified in production, firing once with the right values and respecting consent.
On Google Play the checklist also asks for a staged rollout: release to a small percentage first and halt if the crash or ANR rate rises. That applies to updates, not to a first release.
What I'd do differently
- Turn more of the list into automated checks. Some items, like the upgrade path and the repeated-request test, are repeatable enough that a person should not have to remember them.
- Attach an owner and evidence to each item. A checkbox says someone looked. It does not say what they saw.
- Treat the list as a starting point. It is written for mobile apps with a backend. Some projects will skip items, and a project with payments will need more.
- Keep the store rules fresh. They change, and the "reviewed on" date is only honest if someone updates it.
- Be precise about what the list is. It is my public checklist, not a record that every item was done on every release.
Mohamed Elgohary, Mobile & Product Engineer, Cairo. mohamedelgohary.dev
