If you paid for a mobile app and you are not an engineer, you are in an awkward position: the thing you own is unreadable to you, and the people best placed to assess it are the people who built it.
You do not need to read code to learn a lot. Most of the signals that a codebase is in trouble are visible in the repository, in the release process, or in the answers you get to a handful of direct questions. Here is what to look at.
Signals you can check yourself
These need repository access and no programming knowledge. Ask for read access if you do not have it — and if that request is difficult, you have already learned something.
When was the last commit, and by how many people? Most hosting platforms show a contributors graph. One name is a risk regardless of quality: if that person leaves, your knowledge leaves.
Is there a README that explains how to run the app? Open it. If it is the default text that came with the project template, nobody has written down how any of this works, and the next team starts by reverse-engineering.
Does the repository build automatically? Look for a checkmark or cross against recent commits. No automation at all means every release depends on one person’s laptop being set up correctly.
Are there secrets committed to the repository? Search it for terms like api_key, secret or password. Credentials in the repository are a security problem that outlives everyone who worked on it, because the history keeps them even after they are deleted.
How many open branches are there? A long list of branches last touched months ago usually means work that was started, abandoned and never reconciled.
Questions to ask your team
Ask these plainly. You are not looking for perfect answers — you are looking for whether the answers are specific and unembarrassed.
- If we needed to ship a fix today, how long from decision to it being on someone’s phone? A confident answer measured in hours is healthy. Vagueness, or an answer measured in weeks, means the release process is a problem in itself.
- What breaks most often, and why has it not been fixed? A good team answers this immediately and has a reason. No answer means nobody is watching.
- What would you rewrite if you had two weeks? Every engineer has a list. An empty list means either the codebase is very new or nobody is being honest.
- How do we find out a user hit an error? If the answer is “they email us”, there is no error reporting, and you are learning about failures from your least patient users only.
- Which parts have automated tests? Full coverage is not the goal. But payment, login and anything touching money should have some, and “none” is a real answer to weigh.
- Who else could take this over? Watch for hesitation.
The native folders
A React Native project has ios and android directories containing platform code. These are where undocumented decisions accumulate, because changes there are usually made under deadline pressure to fix a build.
You cannot assess the contents, but you can ask one question: has anyone edited the native projects by hand, and is it written down why? Undocumented native edits are the most common reason a React Native upgrade turns into a month of work — the changes get overwritten and nobody remembers what they did.
Also worth asking whether the project uses patch-package, which applies local fixes to third-party libraries. It is a legitimate tool. But each patch should have a note explaining what it fixes and under what condition it can be removed, otherwise you are carrying modifications nobody dares touch.
Version drift
Ask which version of React Native the app is on, and when it was last upgraded. Then ask what upgrading would cost.
Being a version or two behind is normal and often sensible — upgrading immediately is its own risk. Being many versions behind is different in kind rather than degree: libraries stop supporting your version, security fixes stop arriving, new hires have never used it, and the eventual upgrade becomes a project rather than a task.
The answer you want is a specific plan or a specific reason, not “we should probably do that”.
What is not a warning sign
This matters as much as the rest, because a checklist with no false positives becomes a weapon against a team doing perfectly good work.
- Not being on the newest version of everything. Deliberate lag is a legitimate engineering decision. What matters is whether it is deliberate.
- Low test coverage on interface code. Testing what a screen looks like has poor returns. Testing what happens when a payment fails does not. The number alone tells you nothing.
- Known bugs on a list. A visible list of known problems is evidence of a team that tracks them. The absence of a list is more worrying than a long one.
- Code you find ugly on a screen share. Readability conventions vary and consistency matters more than beauty.
- An estimate that turned out wrong. All estimates are wrong. A pattern of estimates that are wrong in the same direction and never revised is the actual signal.
If several of these land
Do not start with a rewrite. Rewrites are the most expensive available response and they usually reproduce the original problems with newer dependencies, because the problems were rarely in the code.
The cheaper order is nearly always:
- Get error reporting in place, so you learn about failures before your users tell you.
- Automate the build, so releasing stops depending on one person.
- Write down how the thing runs, so knowledge stops being a single point of failure.
- Then fix the specific problems the first three steps have made visible.
Most codebases that feel unsalvageable are ordinary codebases with no visibility. Fix the visibility and the size of the real problem usually turns out to be smaller than the fear of it.
If you have run through this and want a second opinion from someone who did not build it, that is a reasonable thing to want — see how we work, or the performance post if the symptom you are chasing is that the app feels slow.