Mobile App Modernization
The app still works, and it is one store policy change away from being unshippable. That is a narrower position than it looks.
- Typical duration
- Four to twelve months
- Team shape
- Two to five senior engineers
- Starts with
- A three-week assessment

An app that cannot ship a fix is already broken
Mobile decay is quieter than web decay and has a harder edge. Nothing visibly breaks — the app keeps running on installed devices — right up until a store requirement, an OS release or an expired certificate means you cannot publish an update at all.
At that point every fix, including a security one, is blocked behind a modernization you have not started. So the work is sequenced by risk rather than by appetite: first restore the ability to ship, then the dependencies and the target API, then the architecture. Feature work continues throughout, because a roadmap freeze is the thing these programmes actually die of.
What usually goes wrong, and what we do instead
The technical work is well understood. The sequencing and the freeze are what go wrong.
Where ageing apps get stuck
- A target API left behind until the store refuses new submissions
- Dependencies too old to update without breaking three other things
- A build that only works on one machine belonging to someone who left
- No tests, so nobody can tell what a dependency bump broke
- A roadmap freeze announced for the rebuild, then quietly extended
How we run it instead
- The ability to ship restored first, before anything else is touched
- Dependencies moved one at a time, each release verified in the field
- The build reproduced in CI from a clean checkout in week one
- Characterisation tests written before behaviour is changed
- Feature work continues, with slices sequenced around it
What changes, in practice
Not a technology list. The things that are different for the people who build and ship the app.
The app today
Working, and unshippable.
- A release takes weeks, and most of it is fighting the build
- The build works on one machine and nobody is sure why
- Dependencies are years behind and coupled to each other
- A store policy change is an emergency rather than a task
- There are no tests, so a small change is a large risk
- Crashes are reported by users rather than by monitoring
The app after
Same product, shippable.
- A release is a pipeline run and a staged rollout
- The build runs from a clean checkout in CI, reproducibly
- Dependencies are current, and updating them is routine
- Store requirements are tracked, so a deadline is never news
- Behaviour is pinned by tests written from the original app
- Crashes surface on a dashboard before support hears about them
What the work involves
Modernizing an app is four activities, in a deliberate order.
Restoring the release path
A reproducible build in CI, signing that does not depend on one laptop, and a staged rollout you can halt.
Characterisation
Tests written against what the app actually does, including the behaviour users now depend on.
Policy and dependency currency
Target API, permissions, privacy declarations and libraries brought current, one verifiable step at a time.
Incremental architecture
New screens on the new pattern alongside the old ones, rather than a parallel rewrite nobody can ship.
Device range recovery
Re-established support for the hardware your users actually carry, tested rather than assumed.
Backend compatibility
Old installed versions kept working while the API moves, because they will not all update.
How the programme runs
The same five stages as every engagement, applied to an app that cannot stop shipping.
Diagnose
Whether you can ship today, what the store will require next, and which installed versions are still in the field.
Decide
The order of work, sequenced by risk rather than appetite, written down with what each step unblocks.
Prove
One release shipped from a clean CI build to a staged rollout, before any behaviour is changed.
Deliver
Dependencies, policy and architecture moved in slices, each verified in the field before the next.
Hand over
Your team ships a release with us watching, then without us.
What we work with
The starting point is whatever is already there. This is the range we bring apps up to.
Native
- Kotlin
- Swift
- Jetpack Compose
- SwiftUI
Shared codebase
- React Native
- Flutter
- TypeScript
Delivery
- GitHub Actions
- Gradle
- TestFlight
- Sentry
Services
- Node.js
- PostgreSQL
- Firebase
- AWS
What you get, and when
Handed over as it is produced, not assembled at the end.
- Whether you can ship today, and what it would take to
- The store requirements coming, and what each one blocks
- A work order sequenced by risk, with what each step unblocks
- A reproducible build running in CI from a clean checkout
- Characterisation tests pinning the original behaviour
- A staged release you can halt, per slice
- Crash and performance dashboards with alerting
- A release runbook your team has used with us
- A record of which installed versions are still supported
The rest of our mobile work
A mobile engagement rarely stops at one of these. These are the ones next to it.
Related work
Engagements where the product could not stop shipping while it changed.
What we work towards
ILLUSTRATIVE, pending sign-off. These are the measures a mobile modernization is judged on and the direction they should move — not results from a named engagement. They will carry real figures or be removed before launch.
Time to ship a hotfix
Machines that can build a release
Store policy readiness
Behaviour covered by tests
Crashes found before users report them
Questions about mobile modernization
The ones we are asked most often before a first conversation.
Should we just rebuild the app?
We literally cannot publish an update right now. Where do you start?
Do we have to pause feature work?
What about people on very old versions?
Should we move to a shared codebase while we are at it?
How long does this take?
App you cannot update?
Tell us what is blocking a release. We will tell you what it takes to ship again.

