Inheriting someone else's code is stressful, especially when the bills keep coming and nobody can say what's broken. The good news is that a one-hour legacy code audit, done by you, will usually tell you whether you face a set of fixes or a real rebuild. Do it before you approve more spend, hire more developers or sign a new agency.
This guide is for new CTOs and for acquirers doing technical due diligence. It covers the checks, the common causes, the fix-or-rebuild call, how to protect yourself, and what a proper rescue's first week looks like.
The one-hour legacy code audit
You need read access to the repository, a laptop and a timer. Don't read the code line by line yet. You're looking for signals, not bugs.
Minutes 0–10: Build it from a clean clone
Clone the repo onto a fresh machine and follow the README. Can you run the app locally?
- Good: one or two commands, a working app in under 30 minutes, and a sample env file with no real secrets in it.
- Bad: no README, missing env variables, or "ask Dave for the config." If setup takes more than a day, every new hire pays that cost.
Minutes 10–20: Dependencies and known vulnerabilities
Run the audit tool for the stack. That means npm audit for Node (see the npm audit documentation), pip-audit for Python, bundle-audit for Ruby or composer audit for PHP. Then check the framework version against the vendor's support policy.
- Good: framework on a supported major version, and no critical findings, or only a handful with clear upgrade paths.
- Bad: two or more major versions behind, or an end-of-life runtime. Also bad: critical findings that appear in CISA's Known Exploited Vulnerabilities catalog.
For any finding you don't recognise, look up the CVE in the NIST National Vulnerability Database. A critical CVE in a dev-only tool matters far less than one in your auth library.
Minutes 20–30: Tests and CI
Run the test suite. Then open the CI configuration, such as GitHub Actions, GitLab CI or Bitbucket Pipelines.
- Good: tests pass, CI runs on every pull request, and a failed build blocks the merge.
- Bad: no tests, tests that were last green a year ago, or CI that runs but is ignored. Coverage percentage matters less than whether payments, login and core data writes are tested at all.
Minutes 30–40: Read the git history
Two commands tell you a lot. git shortlog -sn --since="1 year ago" shows who wrote the code. This one shows which files change most:
git log --since="12 months ago" --name-only --pretty=format: | sort | uniq -c | sort -rn | head -20
- Good: several active contributors, clear commit messages and changes spread across modules.
- Bad: one person wrote most of last year's commits, messages say "fix" or "wip", and the same three files change every week. Those files are your hot spots.
Minutes 40–50: Secrets and configuration
Run gitleaks or trufflehog across the full history, not just the current branch. A key deleted last year is still in git.
- Good: secrets live in a vault or the cloud provider's secret manager, and nothing turns up in the scan.
- Bad: live API keys, database passwords or private signing keys in the history. Treat every one as compromised and rotate it.
Minutes 50–60: Size and shape
Run cloc to count lines by language. Then list the 10 largest source files.
- Good: most files under a few hundred lines, and a folder structure you can explain to someone else.
- Bad: files of 3,000+ lines, business logic inside UI components, or three languages doing the same job.
Write each result down as green, amber or red. Ten minutes per check keeps you honest.
If you want an outside read before committing budget, a free code or performance audit gives you a second opinion at no cost.
The most common causes, ranked by how often we see them
This ranking is from our own rescue work, not an industry survey. Most troubled codebases have two or three of these at once.
- Stale dependencies. Upgrades were skipped for years because nothing seemed broken. Each skipped version makes the next upgrade harder and riskier.
- No tests, or tests nobody trusts. Without them, every change is a gamble. Teams slow down because they're scared, not because the code is hard.
- Knowledge in one head. One developer or one agency engineer understood the system. When they left, the documentation left with them.
- A tangled data model. Missing foreign keys, duplicated tables and JSON blobs that should be columns. This causes the bugs that are hardest to trace.
- The wrong architecture for current scale. Examples include microservices for a five-person team, or a single server for traffic it was never built to carry. This is the least common cause, but the most expensive.
Fix or rebuild: how to tell
Rewrites are tempting because new code feels clean. They typically take longer than planned and recreate old bugs. We start from the assumption that you fix, and you have to prove a rebuild.
Usually a fix
- Dependencies are behind but have documented upgrade paths.
- Tests are missing but the code is structured enough to add them around hot spots.
- Performance problems trace to a few queries or endpoints.
- The data model is messy but the core entities are right.
Usually a rebuild, or a staged replacement
- The runtime or framework is end-of-life with no realistic migration, such as AngularJS or an unsupported PHP version on a large app.
- The data model is wrong at the core, so every feature fights it.
- Security problems are structural, such as auth logic spread across dozens of files.
- The cost of a fix plan comes close to the cost of a rebuild. In our experience that line sits around 60–70% of rebuild cost.
Even when a rebuild wins, replace in stages. Put a new service behind the old one and move one module at a time. A big-bang cutover is where most rewrites fail.
Protect yourself if the agency relationship is failing
Secure access before you raise concerns, not after. Relationships that are going badly tend to get worse once notice is given.
- Repositories: the repo should live in your GitHub, GitLab or Bitbucket organisation, with you as owner. Being a collaborator is not enough.
- Cloud accounts: you should hold the root or organisation owner login for AWS, Azure or GCP, with billing in your name.
- App stores: your company should own the Apple Developer and Google Play Console accounts. Transferring an app later is possible but slow.
- Domains and DNS: the registrar account should be in your name. Losing a domain can take your email down too.
- Third-party services: payments, email, analytics and error tracking. List every one and confirm who holds the admin login.
- Documentation: ask for architecture notes, deploy steps and environment variable lists now, while people still answer.
On the contract side, check three clauses. IP should transfer on payment, not on project completion. There should be a handover obligation with a defined notice period. Termination should not depend on paying disputed invoices first. Have a lawyer read it; we're engineers, not counsel.
What the first week of a rescue engagement looks like
A rescue week should produce clarity, not features. Here is how we typically run it.
- Day 1: Access and safety. Confirm ownership of every account. Rotate any leaked or shared credentials. Freeze new feature work.
- Day 2: Reproduce. Get the app building locally and in a staging environment. Confirm that backups exist and can actually be restored.
- Day 3: Scan. Run dependency, secret and static analysis tools. Map the hot spots from git history into a risk register.
- Day 4: Read and listen. Walk the critical paths, such as login, payments and core data writes. Talk to support staff about what users complain about.
- Day 5: Report. Deliver a written plan that covers the fix-or-rebuild call, ranked risks, cost ranges and the first 30 days of work.
If anyone proposes shipping features in week one, push back. You can't steer a system you haven't measured.
FAQ
How long does a legacy code audit take?
Your own first pass takes about an hour using the checks above. A professional audit typically takes three to ten working days, depending on size and number of services. Large platforms with several apps can take longer.
Should I rewrite legacy code from scratch?
Usually not. Rewrites take longer than estimated and lose years of quiet bug fixes. Rebuild only when the framework is end-of-life or the data model is wrong at its core, and even then replace it in stages.
What should technical due diligence check before an acquisition?
Check code ownership and licences, dependency health, known vulnerabilities, test coverage on critical paths, and key-person risk. Also confirm that the company, not a contractor, owns the repos, cloud, app store and domain accounts. Gaps there can delay or reprice a deal.



