Inherited a Codebase From a Previous Developer? A Technical Audit Checklist

Before you change an inherited codebase, audit it for five working days. This checklist gives the commands to run on a React and TypeScript app and what healthy and red-flag results look like for builds, dependencies, secrets, types, tests and CI. It ends with a way to compare remediation hours against a rebuild before you decide.

14 min read
A five-day timeline from build to report, branching into fix, refactor or rebuild.
A five-day timeline from build to report, branching into fix, refactor or rebuild.
Contents (10)

If you've inherited a codebase from a previous developer, don't change it yet. Spend five working days auditing it first.

Prove it builds from a clean clone, scan dependencies, licenses and secrets across the full git history, and measure types, tests and CI. Then find the files that change most, and write a one-page findings report that decides between fix, refactor and rebuild.

This checklist is for a CTO or technical founder who has been handed a React and TypeScript codebase by a departed employee, a freelancer or a previous vendor. Every check has a healthy result and a red flag, and most have a command you can run.

What should you do first when you inherit a codebase?

Freeze non-urgent changes and confirm you control every account the product runs on. Then block five working days for the audit before you promise anyone a date.

Technical debt is the normal state of software, not the exception. In the 2024 Stack Overflow Developer Survey, 62.4% of professional developers named the amount of technical debt as a frustration at work. It was the most common answer (Stack Overflow, 2024). Inherited code arrives with that debt and without the context behind it.

Before Day 1, check that you have these in your own organization's name:

  • The repository, with its full commit history rather than a zip file or a single squashed commit
  • CI, hosting and the domain registrar
  • Error tracking and logs
  • Every secret and environment value the app needs to run

If any of these is missing or still sits in the previous developer's personal account, recover it before you audit. Our 30-day recovery protocol for a failed project covers getting accounts back and triaging a stalled project. This checklist is the deep version of that protocol's audit phase.

The audit runs in five one-day blocks:

  1. Day 1, build

    Get from a fresh clone to a running app, and check runtime and framework versions.

  2. Day 2, dependencies and secrets

    Scan for vulnerabilities, package age, licenses and leaked credentials across the history.

  3. Day 3, code health

    Measure types, lint output, file size and risky patterns.

  4. Day 4, tests and delivery

    Check coverage on the paths that matter, CI gates, deployment and rollback.

  5. Day 5, hotspots and report

    Find where change will hurt, then write the findings report.

Write each finding down as you go, with the command, the output and a severity. Don't fix anything during the week, with two exceptions. A live production credential in the history gets rotated the day you find it. A critical vulnerability in shipped code gets patched the same day.

Day 1: Does the app build from a clean clone?

Clone the repository onto a clean machine or container and follow only the README. If you can't install, build and run the app within a day, that's finding number one.

bash
git clone <repo-url> audit && cd audit
git rev-list --count HEAD          # how much history you actually received
cat .nvmrc 2>/dev/null; npm pkg get engines
node -v
npm ci                             # installs exactly what the lockfile says
npm run build && npm test

npm ci fails when the lockfile is missing or out of sync with package.json. That failure matters: without a lockfile, two installs a week apart can produce two different apps. If the repo commits pnpm-lock.yaml or yarn.lock instead, use that tool's frozen install and its audit command throughout. That's pnpm install --frozen-lockfile, yarn install --immutable, or --frozen-lockfile on Yarn 1.

The Node.js project says production applications should only use Active LTS or Maintenance LTS (long-term support) releases. It lists Node.js 20, 18 and 16 as end-of-life (Node.js, 2026). An app pinned to one of those runs without upstream security fixes.

Then look for react-scripts in package.json. That means Create React App, which the React team deprecated on February 14, 2025, leaving it in maintenance mode (React, 2025). It isn't an emergency, but schedule a migration to a framework or a build tool such as Vite.

Finally, check the React version and how much code uses APIs that React 19 removed. These include ReactDOM.render, findDOMNode, string refs, and propTypes and defaultProps on function components (React 19 Upgrade Guide, 2024).

bash
npm ls react react-dom
grep -rnE 'ReactDOM\.render|findDOMNode|\.propTypes|defaultProps|\bref="' src | wc -l

Class components can keep defaultProps, so check those hits before counting them. Zero hits clears the most common blockers, though your dependencies still need versions that support React 19. Hundreds mean the upgrade is a project, not a task.

The greps and find commands in this checklist assume a src/ folder. If the code lives in app/, pages/ or components/, search those instead.

CheckHealthyRed flag
Clean installnpm ci succeeds with the committed lockfileNo lockfile, or setup needs steps nobody wrote down
Build and testsBoth run from the README aloneOnly work after a call with the previous developer
Node versionActive or Maintenance LTS, pinned in .nvmrc or enginesEnd-of-life major, or no pin at all
Build toolingA maintained framework or build toolreact-scripts (Create React App)
HistoryFull commit historyOne squashed commit, or a zip file

Day 2: What's hiding in the dependencies and git history?

Scan four things: known vulnerabilities, package age, licenses and leaked secrets. Scan secrets across the whole history, because deleting a secret in the latest commit leaves it in every earlier one.

Black Duck's 2026 Open Source Security and Risk Analysis report audited 947 commercial codebases (Black Duck, 2026):

87%contained at least one vulnerability
44%contained critical-risk issues, such as flaws that allow remote code execution
92%contained components four or more years out of date
68%contained open source license conflicts

Run these from the repository root:

bash
npm audit --omit=dev                     # known vulnerabilities in what ships
npm outdated                             # how far behind each package is
npx license-checker --production --summary   # licenses in what ships
npx knip                                 # unused files, exports and dependencies
gitleaks git -v .                        # secrets across every commit
trufflehog git file://. --results=verified   # secrets that still work

Gitleaks and TruffleHog are separate installs, not npm packages. TruffleHog's verification step contacts each credential's provider to test whether it still works.

Sort vulnerabilities by what ships to production first. A concrete example is CVE-2025-29927, a critical middleware bypass in Next.js, patched in versions 12.3.5, 13.5.9, 14.2.25 and 15.2.3 (Vercel, 2025). A self-hosted Next.js app below the patched release for its version line is a critical finding. The risk is highest when middleware enforces authentication.

Treat every secret in the history as live until proven otherwise. GitGuardian found that more than 64% of secrets confirmed valid in 2022 still worked when retested in January 2026 (GitGuardian, 2026). The fix is to rotate the credential at the provider first, then remove it from the code.

License findings need a different owner. Copyleft licenses, such as the GPL, can require you to publish your own source. Copyleft in shipped code, or packages with no identifiable license, are questions about who owns the code and what you can do with it. Have counsel read anything the scan flags.

CheckHealthyRed flag
VulnerabilitiesNo critical or high findings in production dependenciesCritical findings in shipped code
Package ageCore packages within one or two majors of currentCore packages years behind, or abandoned
LicensesAll identified, all compatible with how you shipCopyleft in shipped code, or unknown licenses
SecretsNo hits, or every hit already rotatedWorking credentials anywhere in the history

Day 3: How healthy is the code itself?

Measure the code instead of reading it file by file. Type errors, type-safety escape hatches, lint output and file sizes give you a baseline you can track after every change.

bash
npx tsc --noEmit | grep -c "error TS"      # type errors today
npx tsc --showConfig | grep strict         # is strict mode on?
# Vite projects: add -p tsconfig.app.json to both tsc commands
grep -rnE ': any\b|\bas any\b|@ts-ignore|@ts-expect-error' src | wc -l
npx eslint . | grep -E '[0-9]+ problems? \('   # lint problem count
find src \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" \) -print0 | xargs -0 wc -l | sort -rn | head -11
grep -rn "dangerouslySetInnerHTML" src
grep -rhoE '(VITE|NEXT_PUBLIC|REACT_APP)_[A-Z0-9_]+' src | sort | uniq -c

The last command lists every client-prefixed variable the code reads. Variables with those prefixes are compiled into the JavaScript that ships to every visitor's browser. Vite's documentation says they shouldn't hold API keys (Vite). If the list includes anything named like a secret, a private key or a paid API token, that value is public.

dangerouslySetInnerHTML isn't automatically a bug. Each use should render sanitized or trusted content, and you should be able to say which.

CheckHealthyRed flag
Type errorsZero from tsc --noEmitThe build skips type checking and tsc reports errors
Strict mode"strict": trueOff, or on with hundreds of ignores
Escape hatchesA handful of any, each explainedany and @ts-ignore across most files
LintRuns in CI with near-zero errorsNo config, or thousands of problems
File sizeComponents mostly a few hundred lines or lessSeveral files over 1,000 lines
Client env varsOnly public values use client prefixesPrivate keys with client prefixes

Day 4: Do the tests and the pipeline catch anything?

Check three things: how much code the tests execute, whether they cover the paths that make or lose money, and whether CI blocks a bad merge. A suite that passes but never runs on pull requests protects nothing.

bash
npx vitest run --coverage        # or: npx jest --coverage

For a threshold, Google's testing guidance treats 60% coverage as acceptable, 75% as commendable and 90% as exemplary (Google Testing Blog, 2020). The same authors warn that high coverage doesn't guarantee high-quality tests. So read the per-file report, not just the total.

Then open the CI configuration and the repository settings. Confirm that CI runs on every pull request, that the main branch is protected, and that merging requires passing checks.

Finally, find out how a release actually happens. You want one scripted command, a rollback you've seen work, and a staging environment configured like production. If the biggest gap is missing tests, that work can go to a dedicated quality assurance and testing team.

CheckHealthyRed flag
Coverage60% or more, including login, payments and permissionsUnder 60%, or critical paths untested
CILint, types and tests run on every pull requestMissing, disabled or routinely skipped
Branch protectionMain requires passing checks and a reviewAnyone can push straight to main
DeploymentOne scripted, repeatable commandManual steps that one person knew
RollbackDocumented and testedNever tried

Day 5: Where will changes hurt most?

Files that change often, are large and have few tests are the riskiest places to change. Git history tells you which files change most, in seconds.

bash
git log --since="12 months ago" --name-only --format="" | grep -v '^$' | sort | uniq -c | sort -rn | head -20
git shortlog -sn --since="12 months ago" HEAD

The first command lists the 20 most-changed files in the last year. Cross it with the largest files from Day 3 and the coverage report from Day 4. Files on all three lists are your hotspots. Put characterization tests around them, meaning tests that pin down what the code does today, before anyone changes them.

The second command shows who wrote the recent history. If one person wrote most of it and has left, every hotspot is also a knowledge gap. If the history was squashed, both commands return nothing useful, and that belongs in the report as its own finding.

CheckHealthyRed flag
HotspotsA few, each covered by testsMany, none tested
Knowledge spreadSeveral active contributorsOne departed author wrote most of it
HistoryFull and readableSquashed or missing

How do you decide between fix, refactor and rebuild?

Put every finding in a one-page report, estimate each as a range of hours, and compare the total with a rebuild estimate for the same scope. Rebuild only when remediation approaches the cost of a rebuild, or when the findings show the product's design is wrong rather than its code.

The one-page report holds five things: the commit you audited, a findings table, the hotspot list, remediation and rebuild totals, and a recommendation. When the recommendation matters more than the code, the decision itself can be the engagement: technology and innovation consulting ends in a documented direction you can hand to any team, including your own.

Here's a worked example. The numbers are illustrative estimates for a hypothetical 38-screen Next.js app, not results from a real audit.

SeverityFindingEvidenceEffort (hours)Decision
CriticalLive payment key in git historyTruffleHog verified result2-4Rotate today, then remove
CriticalSelf-hosted Next.js 14.2.10, below the patched 14.2.25npm ls next4-8Patch today
HighNode.js 18 in production.nvmrc and hosting config8-16Upgrade to a supported LTS
HighLogin and checkout untestedCoverage report16-24Add characterization tests
MediumStrict mode off, 900 escape hatchestsc --showConfig, grep count40-80Enable folder by folder
MediumThree hotspot files over 1,000 linesChurn list, wc -l24-38Split once tests exist
Total remediation94-170

The low end is 2 + 4 + 8 + 16 + 40 + 24 = 94 hours. The high end is 4 + 8 + 16 + 24 + 80 + 38 = 170 hours.

For the rebuild, assume 20-30 hours per screen for the front end, API wiring and tests. That's 38 × 20 = 760 hours at the low end and 38 × 30 = 1,140 hours at the high end, before data migration.

Now compare the ranges. The best case is 94 ÷ 1,140, or about 8% of a rebuild. The worst case is 170 ÷ 760, or about 22%.

Even the pessimistic remediation estimate is under a quarter of the optimistic rebuild. That's a clear fix-and-refactor decision.

Here's a rule of thumb, not an industry standard. When the high remediation estimate reaches half the low rebuild estimate, cost the rebuild properly before choosing. Rebuilds also hide a cost the estimate misses. Every behavior the old code handled quietly has to be rediscovered and rebuilt.

Fix and refactor when

  • The business logic is right and users depend on it
  • Problems cluster in a few hotspots
  • The runtime and framework have a supported upgrade path
  • Remediation is a small share of a rebuild

Rebuild when

  • Requirements have changed so much that the old behavior isn't wanted
  • The data model can't represent what the business now needs
  • The stack has no supported upgrade path
  • Remediation approaches the cost of a rebuild

If you'd like a second opinion on a findings report before you commit a budget, a free 30-minute scoping call is a quick way to pressure-test it. If someone else will do the remediation, use these questions that get honest answers from a vendor before you hand it over.

When is a five-day audit the wrong move?

A five-day audit is the wrong move when the codebase is tiny, production is down, or you don't control the accounts yet. In those cases it either costs more than it saves or answers the wrong question first.

  • The codebase is tiny. A few thousand lines can be read end to end. Run the Day 2 scans, read the code and skip the rest.
  • Production is down or data is at risk. Stabilize first. The 30-day recovery protocol puts getting the accounts back ahead of any audit.
  • You don't control the accounts. Recover access first.
  • The product is being retired within months. Only the Day 2 security checks matter. Rotate secrets, patch criticals and stop there.
  • Nobody technical will act on the output. Commission an independent review instead, and ask for findings in the table format above.

Handover and re-audit questions

What should you ask the previous developer before they leave?

Ask for what the code can't tell you. That means how to deploy and roll back, where every account and secret lives, what's half-finished, and which parts they avoided touching and why. Get the answers in writing, and record one deploy while they walk you through it.

Should you re-run the audit later?

Yes, but automate most of it. Put the vulnerability, secrets, type, lint and coverage checks into CI so they run on every pull request. Repeat the full five days before a major upgrade or the next change of ownership.

What should you have at the end of the week?

By Friday you should have a findings report, a list of hotspots and a remediation estimate you can compare with a rebuild. The first changes should be the critical findings, followed by tests around the hotspots.

Newsletter

New writing, when there is something worth sending

Occasional notes on architecture and delivery practice — the same things we write about here, sent when a piece is published. Roughly once a month, never more.

Your address is stored to send you this newsletter and nothing else. Every email has an unsubscribe link.