Rescuing a stalled or inherited software project
How a good team audits, stabilises, and takes over a half-built or abandoned codebase without a needless rewrite, and how to spot what is salvageable.

The freelancer stopped replying. The agency keeps quoting "two more weeks." Your CTO left and took the only mental model of the system with him. Now you are staring at a repo nobody fully understands, a half-built product, and a nagging fear that the only way out is to throw it all away and start again.
Usually, it is not. We take over messy and inherited codebases for a living, and most of them are more salvageable than they look. A panicked rewrite is the expensive option, and it is the one a tired founder reaches for first. Before you do anything drastic, you want a clear-eyed read on what you actually have. That starts with an audit, not a teardown.
Step one: an honest audit before any code
You cannot fix what you have not measured. The first thing a good team does is spend a few days reading, not writing. Not promising. Not estimating. Just reading.
A real audit answers a small set of blunt questions:
- Does it run? Can a new engineer clone the repo and get it working locally in an afternoon, or does it take a week of tribal knowledge? A project nobody can run is a project nobody can safely change.
- Does the git history make sense? A clean history tells a story. A single "initial commit" with 40,000 lines tells a different, sadder one.
- What does the data look like? The database is usually the truest record of what the app really does. Schemas lie less than code comments.
- Where are the tests, if any? You do not need 90 percent coverage. You need enough of a safety net to change one thing without breaking three others.
- What is the dependency situation? Outdated packages, abandoned libraries, and pinned-to-nothing versions are where the security holes and surprise breakages live.
At the end of an audit you should get a plain-language report: what is here, what works, what is fragile, what is genuinely broken, and what it would take to make it boring and reliable. If a team wants to start rebuilding before they can tell you that, be careful. The most useful deliverable in week one is understanding, and a technical audit and consulting engagement is the low-commitment way to buy it before you sign up for a big build.
What is usually salvageable (and what is not)
Here is the part that surprises people. The things founders panic about are often fine, and the things they ignore are often the real problem.
Usually salvageable: the database and its data, the core business logic, and the parts of the app real users actually touch every day. Ugly-but-working code is an asset. It encodes hard-won decisions about your actual business, and rewriting it from scratch means relearning all of those lessons the hard way.
Often worth keeping with work: the front end, even a dated one. A tired React app can be modernised incrementally. You rarely need to bin a working interface to move to current Next.js and React patterns; you migrate it piece by piece while it keeps serving users.
Genuinely not worth saving: code with no tests, no docs, and no original author, where the logic is so tangled nobody can predict what a change will do. Build tooling that no longer installs. Half-finished features that were never wired up. And anything on a framework version so old it no longer receives security patches and cannot be upgraded in place.
The line is not "old" versus "new." It is "understandable" versus "a black box nobody dares touch." Old code you can reason about is worth more than shiny code that surprises you.
Stabilise first, improve second
Once you know what you have, resist the urge to refactor everything. The first job is to make the system safe to work in. In practice that means a short, unglamorous sequence:
- Get it building and deploying reliably. A repeatable build and a one-command deploy are the foundation for everything else. If shipping is scary, every fix is scary.
- Add a thin layer of tests around the critical paths. Not everything. The checkout. The login. The thing that, if it breaks, your phone rings. This is the seatbelt that lets you change code without holding your breath.
- Patch the urgent security and dependency issues. The known holes, the leaked keys in the repo history, the packages with public advisories.
- Write down how it works. Even a single README that explains how to run it, where things live, and what the gotchas are turns a black box into a system.
Only after that do you talk about new features or a redesign. Building on an unstable base is how projects stall in the first place. Getting a codebase to this calm, predictable state is the bulk of what good ongoing maintenance and support actually buys you.
The rewrite question, honestly
People expect us to say "never rewrite." We will not. Sometimes a full rebuild is the right call: when the framework is genuinely dead, when the data model is so wrong every feature fights it, or when the cost of understanding the old system exceeds the cost of replacing it.
But a rewrite is a serious decision, not a default. It means months with no new features, the constant risk of recreating old bugs, and a long stretch maintaining two systems at once. Most of the time the better path is the strangler approach: wrap the old system, replace it one piece at a time, and keep shipping value the whole way through. If a rewrite is truly warranted, the audit will make that obvious, and any honest team will show you the reasoning rather than just asserting it. The decision deserves the same scrutiny you would give any build versus buy choice.
What good looks like, and the red flags
When you interview a team to rescue your project, the difference between a safe pair of hands and another stalled engagement shows up early.
Good looks like: a team that asks to see the code and the data before quoting. A short paid audit before any big commitment. Plain-English explanations of what is wrong and why. A willingness to keep your working code instead of reflexively trashing it. Small, frequent deployments rather than a big-bang reveal in three months. And honesty about what they do not yet know, because nobody understands an inherited system perfectly on day one.
Red flags: "We'd just rebuild it" before they have read a single file. A fixed price quoted sight-unseen. Disparaging the previous developer to win your trust (today's hero is tomorrow's villain). Long silences and "almost done." Resistance to writing anything down, which usually means they are building the next black box you will have to escape later.
The pattern is simple. A senior team derisks the handoff by making the system understandable and shippable before they make it bigger. A weaker one starts swinging a hammer because rebuilding feels productive.
The takeaway
An inherited or stalled codebase is rarely as doomed as it feels at 11pm. The way out is almost never a panicked rewrite. It is a calm audit, an honest read on what is salvageable, and a stabilise-first plan that gets you shipping safely again. Keep what works, replace what cannot be understood, and write down enough that the next person is not flying blind.
This is the work we genuinely enjoy: taking something tangled and abandoned and turning it back into a product you can build on. If you have a project that stalled or landed in your lap, tell us what you are dealing with and we will give you a straight answer about what it would take to rescue it. That is the good kind of lazy: doing the boring diagnosis first so you never pay for the same software twice.

