The complaint usually arrives as a feeling rather than a bug report. The app is fine. It works. It just feels old, ships slowly, and every small change takes three weeks and breaks something else.
That feeling is a real signal, and it is worth diagnosing properly before anyone says the word rewrite.
The symptoms that actually indicate rot
Most apps that feel dated are suffering from one of four things, and they need different responses.
Visual drift. The app follows a design language two OS generations old. Fonts, spacing, navigation patterns, and animations all say a specific year. This is the cheapest problem to fix and the one most often misdiagnosed as needing a rebuild.
Slow delivery. Every change takes longer than it should. Nobody wants to touch certain files. Estimates have quietly tripled over three years. This is architecture, and it does not improve on its own.
Platform lag. You cannot adopt new OS capabilities because the codebase predates them, and each annual release costs more to absorb than the last.
Performance decay. The app got slower as features accumulated, cold start crept up, and the crash rate on older devices climbed.
Only the second and third genuinely argue for structural work. The first and fourth are usually fixable inside the app you have, at a fraction of the cost.
The question that decides it
Can you ship a meaningful change safely in a normal sprint?
If yes, the app has a UI problem or a performance problem, and you should fix those directly. A redesign on a healthy codebase is weeks, not quarters, and carries almost no risk.
If no — if every change requires touching code nobody understands, if there are no tests around the parts that matter, if one person is the only one who can safely modify the sync layer — then you have an architecture problem, and no amount of visual work will address it.
That distinction matters because a rewrite is enormously more expensive and risky than teams remember. The app you have handles hundreds of edge cases discovered over years of real usage. Most of them are undocumented. Some of them are load-bearing.

Fig. — Most "we need a rewrite" conversations end at the first branch.
Incremental beats big-bang, almost always
When structural work is genuinely needed, the approach that survives contact with reality is replacing screen by screen rather than all at once.
Both major cross-platform frameworks can be embedded into an existing native app, which means you can rebuild one screen in the new stack, ship it, and leave everything else alone. Users get a gradually improving app. You get real feedback on the new architecture before betting the product on it.
Start with a screen that is self-contained and low-risk — a settings page, a static content view — and deliberately not the checkout. The first migrated screen is where you discover what your build pipeline, your navigation bridging, and your state management actually require.
The alternative — building the replacement in parallel and switching over — sounds cleaner and usually is not. Two codebases means two sets of bug fixes for however long it takes, and the switchover is a single moment where everything either works or does not, in front of every user simultaneously.
The honest exception: if the existing app is small, or genuinely unmaintainable, or built on a framework that no longer receives updates, a clean rebuild may be cheaper. That call needs someone who has read the code, not someone who has read the complaints.
What it costs, and what nobody quotes
Rebuild estimates are consistently too low for one reason: the estimate covers the features you can see, and the cost is in the ones you cannot.
The undocumented business logic. The workaround for the payment provider's quirk. The retry behaviour someone added after a bad week in production. The specific way sync resolves conflicts, which nobody wrote down and which customers depend on.
Budget archaeology as a real phase. Someone reads the old code and writes down what it actually does, including the parts that look like mistakes and are not. Teams that skip this ship a technically superior app that gets worse reviews than the one it replaced, because it lost behaviour nobody knew was important.
The backend usually needs the conversation more
A detail that derails a lot of app modernisation projects: the app is often not the oldest thing involved.
Apps that feel dated are frequently talking to APIs designed a decade ago — chatty endpoints, no pagination, responses shaped for a screen layout that no longer exists, authentication that predates every current standard. Rebuilding the client on top of that gives you a modern app making the same twelve calls to render one screen, and the performance complaint survives the rebuild intact.
Check this before scoping. Open the network log during a typical session and count the requests, their sizes, and how many are round trips that could have been one. If the answer is unflattering, some of your modernisation budget belongs on the server side, and spending it there often delivers a bigger perceived improvement than anything you do to the UI.
It also changes sequencing. An API cleanup can ship independently, benefits the existing app immediately, and de-risks whatever you build next.
Migrating without losing people
Keep the data. Users should open the new version and find their content, their settings, their history. A migration that resets someone's state is indistinguishable from a broken update.
Ship as an update, not a new listing. A new store entry throws away your reviews, your ranking, and every installed user. This mistake is rare and catastrophic.
Roll out gradually. Both stores support staged releases; use them. A crash affecting a niche device is a manageable incident at five percent and a disaster at a hundred.
And do not redesign everything at once. Users tolerate a better version of the app they know. An app that looks entirely different, moved every button, and renamed the sections generates support load and one-star reviews that have nothing to do with code quality.
One last thing worth agreeing before the work starts: what "done" means. Modernisation projects drift because the goal was a feeling rather than a condition. Pick something measurable — cold start under two seconds, a change shippable in a sprint, crash-free sessions above a stated threshold — and stop when you hit it rather than when the budget runs out.
Deciding between a refresh and a rebuild — and then doing it without losing the audience — is the sort of work we do at CODT.


