My App Is Broken: How to Rescue a Web or Mobile App and Get It Production-Ready
What actually happens when a freelance engineer takes over an app that crashes, fails after launch, or only works on one laptop: how problems are found, what usually causes them, and when to repair instead of rewrite.
A lot of the projects that land in my inbox start the same way. Someone built an app, or paid someone to build it, and it mostly works. Then it doesn't. Logins fail for some users. The mobile build crashes on launch. A feature that worked every time during the demo breaks the first week real customers touch it. The original developer is gone, busy, or no longer answering email.
If that is where you are, the situation is usually more fixable than it feels. It is also easy to make worse. This is how I approach these projects and what you should expect from anyone you hire to do it.
Start by reproducing the problem
"It's broken" is not something an engineer can fix. "Users on Android 13 get a blank screen after tapping Sign In, but only on cellular" is. The first job is turning a vague complaint into something I can trigger on demand. That means reading error logs, looking at what the app does on a real device, and asking you for the specific moment you noticed it going wrong.
If there are no logs, which is common, the first change is adding them. Crash reporting and basic request logging are cheap to add and usually pay for themselves within the first week. Without them everyone is guessing, and guessing on a client's budget is how a two-day fix becomes a two-month one.
The causes I see most often
Every codebase is different, but the failures cluster. Most of what I get hired to fix falls into a short list:
- It works on the developer's machine only. Settings, API keys, or database contents exist locally and were never set up in the live environment. The app then fails the moment it is deployed.
- Security rules or permissions that are too strict or too loose. In Firebase this shows up as Firestore or Storage rules that block legitimate users, or rules left wide open during testing that nobody locked down again.
- No plan for slow or missing networks. Mobile apps assume a fast connection, so a spotty one produces frozen screens and lost form data.
- Version drift. A package, SDK, or iOS and Android build tool was updated and quietly broke something that used to work.
- Data that doesn't match the code. Old records are missing fields the new code expects, so a screen crashes for older customers while it works fine for new test accounts.
- Costs or limits nobody planned for. A cloud project hits a quota, a free tier ends, or a billing account lapses, and services stop responding with confusing errors.
When the problem is an AI feature
Apps with an AI feature fail in some extra ways that are worth knowing about. The most common one is a feature that works perfectly in development and breaks in production. Usually the cause is dull: the API key for the model provider was set on the developer's machine but never added to the hosting environment, or it was committed into the app itself where anyone can extract it.
Others I run into: the app sends too much text to the model and hits the context limit on longer conversations, a model version was retired and the request now returns an error, requests have no timeout so the screen hangs when the provider is slow, or the app trusts whatever the model returns and crashes when the response isn't in the expected format. There is also the cost side. An AI call with no limit on how often a user can trigger it can turn into a surprising bill.
A fix here usually means moving the provider call to a server so the key stays private, validating what comes back, adding timeouts and a sensible fallback message, and putting a cap on usage. None of it is exotic. It is ordinary backend discipline applied to a service that happens to be a language model.
I also see apps where parts of the code were generated with an AI coding assistant and never properly reviewed. That isn't a problem in itself. Plenty of good software gets written with help from those tools. The trouble shows up when nobody read the result closely, so there are duplicated functions, missing error handling, or code that passes one happy-path test and nothing else. Reviewing and tightening that kind of code is a normal part of a rescue job.
Fix what is hurting users first
Once the cause is clear, the order of work matters more than people expect. I split it into three buckets: things that stop users from doing the core task, things that risk your data or security, and everything else. The first two come before cleanup, refactoring, or new features, however tempting it is to improve the code while I'm in there.
A small example of the kind of safeguard I add early is a startup check that refuses to run if required settings are missing. It turns a confusing failure at 2 a.m. into a clear message during deployment:
const required = ["FIREBASE_PROJECT_ID", "AI_PROVIDER_API_KEY", "APP_BASE_URL"];
const missing = required.filter((name) => !process.env[name]);
if (missing.length > 0) {
throw new Error(`Missing required environment variables: ${missing.join(", ")}`);
}It is a few lines, and it would have prevented a surprising number of the "works locally, fails live" problems I have been asked to debug.
Repair or rewrite
Clients often ask me whether they should just start over. Sometimes the answer is yes, but less often than people fear. A rewrite throws away all the small decisions embedded in the old code, including the ones that were correct, and it takes much longer than anyone estimates up front.
I lean toward repair when the data model is sound, the main screens work, and the problems are concentrated in a few areas. I lean toward replacing a piece, such as the sync layer or the login flow, when it is the source of most of the pain and the rest can stay. A full rewrite makes sense when the foundation is wrong, for example when the app was built on a framework that is no longer maintained or the data structure can't support what the business now needs. I will tell you which situation you are in after reviewing the code, and I will tell you if the honest answer is that it is not worth saving.
Getting to something you can rely on
An app that stops crashing is not the same as an app that is production-ready. Before I consider a rescue finished, I want a few things in place: automated checks that run before every release, a deployment process that is written down and repeatable, separate test and live environments, error reporting so problems reach us before customers do, and backups for the data that matters.
On Firebase projects that usually also means reviewing security rules, checking that indexes exist for the queries the app runs, and setting budget alerts so a runaway loop doesn't become an unexpected invoice. For mobile apps it means testing release builds on real devices, not only the simulator. None of this is glamorous, but it is the difference between a fix that holds and one that breaks again next month.
What to send me
If you are dealing with a broken or unreliable app, the most useful things you can share are what you expected to happen, what happens instead, who is affected, and when it started. Access to the code repository and the hosting or Firebase project helps a lot, and so do any screenshots or error messages, even if they look meaningless to you. You don't need to know what is wrong or how to describe it technically.
If this sounds like your situation, get in touch with a short description or book a consultation. I work on full-stack web and API problems, Flutter and mobile apps, and AI feature integrations, and I can usually tell you after a first conversation whether you are looking at a quick repair or a larger project.
Production Case Studies & Capabilities
Explore how these engineering patterns are deployed in production systems and available through client engagements.
Full-Stack Engineering
Companies often struggle with fragile web applications, slow delivery cycles, and disjointed client-server boundaries. I build robust, production-grade applications that scale seamlessly from day one without architectural debt.
AI Engineering & LLM Integration
Many AI initiatives fail in production due to prompt fragility, high token latency, hallucination risks, and lack of structured evaluation. I bridge the gap between experimental LLM prompts and deterministic software engineering.
Mobile Engineering (Flutter & Cross-Platform)
Maintaining distinct iOS and Android native codebases doubles development overhead and creates feature parity bugs. Poor offline handling leads to data loss and frustrated users on flaky network connections.
Technical Consulting & Advisory
Making the wrong technology choices, hiring the wrong vendor, or misjudging project scope can cost months of runway and hundreds of thousands of dollars.
Related Technical Articles
Architecting Offline-First Mobile Applications with Flutter and SQLite
A practical overview of offline-first Flutter architecture, local persistence, synchronization, conflict handling, retries, and resilient mobile user experiences.
Deterministic Static Next.js with App Router and Firebase Hosting
Deploy lightweight, static Next.js 16 applications to Firebase Hosting with cacheable CDN delivery, declarative security headers, and no server to manage.