Agency & Business
legacy code audit web mobileTechnical Due DiligenceReactNext.jsFlutterSecurityPerformanceModernization

Technical Due Diligence for Legacy Web and Mobile Apps: Audit Checklist, Risk Scoring, and a Modernization Plan

AO
Adrijan Omićević
·18 min read

# What You’ll Learn#

A technical due diligence for a legacy web or mobile app is not a code review. It is a structured assessment that answers three executive questions: is it secure, is it maintainable, and what will it cost to evolve.

This guide shows a step-by-step process to run a legacy code audit web mobile for a Next.js and React app or a Flutter app. You’ll get a checklist, a risk scoring rubric, and a way to convert findings into a phased modernization plan that leadership can fund.

# When You Actually Need Due Diligence#

You typically need this audit when one of these triggers occurs:

  1. 1
    Acquisition or investment due diligence, where technical risk affects valuation.
  2. 2
    A new agency or internal team takeover, where you need predictable delivery timelines.
  3. 3
    The roadmap is blocked by bugs, regressions, or slow releases.
  4. 4
    Security or compliance requirements increased, including SOC 2, ISO 27001, or PCI-related controls.

In practice, teams underestimate compounding maintenance cost. Multiple industry benchmarks put annual maintenance at roughly 15 to 25 percent of initial build cost, and legacy systems trend toward the high end when test coverage and dependency hygiene are weak. The goal of this audit is to quantify risk, not just complain about old code.

# Scope and Deliverables: Define What “Done” Means#

Before touching the repo, set a concrete scope so the audit does not turn into open-ended refactoring advice.

Audit Scope Checklist#

AreaWeb: Next.js and ReactMobile: FlutterEvidence you should collect
SecurityAuth flows, API calls, secrets, headers, dependency CVEsAuth flows, deep links, storage, dependency CVEsThreat list, CVE report, top fixes
PerformanceCore Web Vitals, bundle size, server response, cachingStartup time, jank, memory, batteryMetrics snapshot and bottleneck list
DX and MaintainabilityBuild reproducibility, linting, tests, CI timeBuild flavors, code gen, tests, CI timeBuild steps, quality gates, pain points
ArchitectureData flow, routing, state, boundariesState management, navigation, modularityDiagram and key risks
Dependency Healthnpm lockfile, major versions, transitive riskpubspec lock, plugin stabilityUpgrade plan and risk log
Ops and ObservabilityLogs, errors, tracing, SLOsCrash reporting, analytics, release monitoringMonitoring gaps and action list

Minimum Deliverables That Stakeholders Understand#

  • A one-page risk summary with a numeric score and top 10 issues.
  • A prioritized backlog with estimates and acceptance criteria.
  • A phased modernization roadmap with timelines and “no rewrite” options.
  • A short technical readout, ideally 45 minutes with Q and A.

ℹ️ Note: A due diligence report without estimates and sequencing is not actionable. If you cannot assign a rough size, leadership cannot budget or plan a release train.

# Step-by-Step Audit Process for Legacy Next.js, React, and Flutter#

This process is designed for 5 to 10 days. If your app is large, keep the same steps but time-box each to avoid boiling the ocean.

# Step 1: Access, Inventory, and Reproducible Builds#

Your first goal is to answer one question: can a new engineer build and run the app in under 60 minutes.

What to Collect in the First Day#

AssetWhy it mattersWhat good looks like
Repository listHidden dependencies derail timelinesAll repos documented, including infra
EnvironmentsBugs often live in configDev, staging, prod described and reachable
Secrets managementLeaked keys create incidentsVault or managed secrets, least privilege
Build instructionsOnboarding time becomes costSingle command start, consistent tooling
CI pipelineRelease speed and stabilityReproducible builds, cached dependencies

Fast Build Repro Checks#

For Next.js and React:

Bash
node -v
npm -v
npm ci
npm run build
npm run test

For Flutter:

Bash
flutter --version
flutter doctor -v
flutter pub get
flutter test
flutter build apk --debug

If any step fails, capture the error and the time spent. That time becomes part of your maintainability score.

💡 Tip: Ask one developer who has never touched the project to run the setup from scratch while screen-recording. You will get a precise list of missing docs, implicit assumptions, and flaky steps.

# Step 2: Architecture and Codebase Map#

You want to quickly understand where change is safe and where change is risky.

What to Document#

For Next.js and React:

  • Routing model and data fetching patterns across pages and components.
  • State management approach, including where server state and UI state live.
  • API layer conventions and error handling.
  • SSR, SSG, and caching decisions, including edge deployment if used.

For Flutter:

  • State management library and its boundaries.
  • Navigation structure and deep link handling.
  • Layering, for example presentation, domain, data.
  • Platform channels and native code touchpoints.

Signals of Hidden Complexity#

SignalWhy it mattersTypical remediation
Shared global state mutated everywhereBugs become non-localIntroduce boundaries and typed events
No module boundariesRefactors become riskyModularize by feature or domain
Business logic in UI layerTesting becomes expensiveExtract services and domain layer
Direct API calls from widgets or componentsHard to secure and cacheCentral API client and interceptors

# Step 3: Security Audit Checklist for Web and Mobile#

Security is usually the highest-impact part of technical due diligence because it can create immediate business risk. Your goal is not to find every issue, but to identify systemic exposure.

Use this as a companion to our deeper security guide: Web Application Security Checklist.

Web: Next.js and React Security Checklist#

CheckWhat to verifyEvidence
AuthenticationToken lifecycle, refresh, logoutFlow diagram and test cases
AuthorizationRole checks are server-sidePolicy list and API enforcement proof
Input validationServer validates, client only assistsValidation schema and error mapping
XSS protectionsEscaping and safe renderingReview dangerous rendering usage
CSRFCookies with proper protectionsHeader and cookie attributes
Security headersCSP, HSTS, X-Frame-Options equivalentsDeployed headers snapshot
SecretsNo secrets in client bundlegrep results and CI checks
Dependency vulnerabilitiesnpm audit is not enoughCVE report with severity and exploitability

Practical checks you can run:

Bash
npm audit --audit-level=high
npx depcheck
npx license-checker --summary

Mobile: Flutter Security Checklist#

CheckWhat to verifyEvidence
Secure storageTokens not stored in plain prefsStorage library usage review
Deep linksValidation, no auth bypassTest cases for malicious links
TLS and API trustNo accepting bad certificatesHttp client configuration
Jailbreak and root signalsIf needed for threat modelDecisions documented
Crash logsNo PII in logsSampling from crash tool
Dependency vulnerabilitiesPlugin provenance and versionsPlugin list and maintenance status

A fast scan of dependencies:

Bash
flutter pub deps --style=compact
flutter pub outdated

⚠️ Warning: A common due diligence failure is auditing only the client code. If the API and identity provider are out of scope, explicitly document assumptions, because most real authorization bugs are server-side.

# Step 4: Performance Audit with Real Metrics, Not Opinions#

Performance findings should be tied to numbers. On web, the most defensible metrics are Core Web Vitals and bundle sizes. On Flutter, focus on jank, memory, and startup time.

Use this for deeper tactics and tooling: Website Performance Optimization.

Web Metrics to Capture#

MetricTarget for most productsHow to measure
LCPless than 2.5 secondsLighthouse, CrUX, RUM
INPless than 200 millisecondsRUM and field data
CLSless than 0.1Lighthouse and field data
TTFBless than 800 millisecondsRUM, server logs
JS bundle sizekeep critical path minimalbuild analyzer and source maps

Quick Next.js checks:

Bash
npm run build
npx next build --profile
npx @next/bundle-analyzer

If you cannot run a bundle analyzer, at least capture the /_next/static output sizes and the number of chunks.

Flutter Metrics to Capture#

MetricPractical targetHow to measure
Cold startless than 2 seconds on mid-range deviceProfile mode, real device
Jankkeep frames under budgetFlutter DevTools frame chart
Memorystable under typical flowsDevTools memory timeline
Networkavoid chatty APIsProxy logs and request counts

Basic profiling command:

Bash
flutter run --profile

🎯 Key Takeaway: Performance audits are credible when you provide a baseline, a top 5 bottleneck list, and an estimate of expected improvement, for example “reduce initial JS by 35 percent by removing unused dependencies and splitting routes”.

# Step 5: Developer Experience and Delivery Pipeline Audit#

DX issues are not “nice to have”. They show up as longer cycle times, more regressions, and higher staffing cost.

What to Measure#

DX AreaWhat to measureWhy it matters
Onboarding timeminutes to first successful runhiring and handover risk
CI durationminutes per PRthroughput and cost
Flakinessreruns per weekmorale and predictability
Code quality gateslint, format, typesregression prevention
Test coverageunit, integration, e2econfidence to change

Practical checks:

  • Is TypeScript strict mode enabled in a React and Next.js app.
  • Is there a single source of truth for environment variables.
  • Are Flutter build flavors documented and reproducible.
  • Is code generation stable, and are generated files committed or not, consistently.

# Step 6: Dependency Health and Upgrade Risk#

Legacy apps tend to accumulate “dependency debt”. A single unmaintained package can block major upgrades, and major upgrades can block security patches.

Dependency Health Checklist#

DimensionWeb: npmMobile: pubWhat to flag as risk
Lockfilepackage-lock or pnpm-lockpubspec.lockmissing lockfile, frequent drift
Outdated majorsnext, react, nodeflutter, dart sdkmultiple major jumps required
Abandonwarelow downloads, no commitsdiscontinued pluginsreplacement required
Transitive risknested packages with CVEsnested packagespatching complexity
LicensesGPL, AGPL exposurerestrictive licenseslegal and distribution risk

Commands that produce actionable output:

Bash
npm outdated
npm ls --depth=0
Bash
flutter pub outdated --mode=null-safety

If the Flutter app still has non-null-safe dependencies, treat it as a modernization priority. Null safety migration reduces runtime crashes and improves tooling, and most active packages have supported it for years.

# Step 7: Testing Strategy and Quality Signals#

You are not aiming for 100 percent test coverage. You are aiming for “safe change” in the highest-risk areas.

Minimum Test Baseline for Legacy Apps#

LayerWeb recommendationFlutter recommendationWhy
Unit testscritical utils and domain logicdomain and servicesfast regression protection
Integration testsAPI client and auth flowsrepositories and statecatches contract breakage
E2E teststop 3 revenue flowstop 3 user flowsprotects business outcomes
Visual checkssnapshot or visual diff for key pagesgolden tests for key widgetsprevents UI regressions

A good due diligence output is a list of “test gaps” tied to the roadmap. Example: “Add E2E coverage for checkout login flow before refactoring auth”.

# Risk Scoring Rubric: Make Findings Comparable#

A risk score turns subjective concerns into a decision tool. Use a weighted model so leadership can see what drives the result.

Scoring Model#

Score each category from 0 to 5, then multiply by weight. Higher is worse.

CategoryWeightScore meaning
Security300 is robust controls, 5 is exploitable and unmonitored
Reliability and Observability200 is strong SLOs and monitoring, 5 is blind incidents
Performance150 meets targets, 5 misses targets severely
Maintainability and Architecture150 modular and readable, 5 tightly coupled
Dependency Health100 current and maintained, 5 blocked upgrades
DX and Delivery100 fast CI and strong gates, 5 slow and flaky

Overall risk score formula:

Risk Score = sum(category_score * weight) / sum(weights)

Severity and Effort Matrix for Each Finding#

In addition to the overall score, each issue should have two ratings:

  • Impact from 1 to 5, where 5 is user data risk or revenue impact.
  • Effort from 1 to 5, where 5 is multi-sprint change with cross-team coordination.

This lets you prioritize by “impact per effort”.

Example Risk Scorecard#

AreaScore 0-5WeightWeighted
Security430120
Reliability and Observability32060
Performance21530
Maintainability and Architecture41560
Dependency Health51050
DX and Delivery31030
Total100350

Overall Risk Score = 350 / 100 = 3.5

Interpretation:

Score rangeRisk levelTypical decision
0.0 to 1.5LowNormal iteration, small improvements
1.6 to 2.9ModeratePlan a modernization quarter
3.0 to 4.0HighStabilization first, pause big features
4.1 to 5.0CriticalImmediate security and reliability response

# Turning Findings Into a Phased Modernization Roadmap#

A roadmap is where most audits fail. You need sequencing that reduces risk early while keeping the product shipping.

Phase 0: Stabilize and Make Change Safe, 1 to 3 Weeks#

Goals:

  • Make builds reproducible.
  • Add basic monitoring and crash reporting.
  • Patch high-severity vulnerabilities with known exploits.

Typical tasks:

TaskApplies toOutcome
Pin runtime versionsWeb and mobilefewer environment-only bugs
CI caching and deterministic installsWeb and mobilefaster builds, fewer flakes
Add error reporting and alertsWeb and mobileless time to detect incidents
Patch critical CVEsWeb and mobilereduced breach risk

Deliverable: a “green pipeline” and an incident visibility baseline.

Phase 1: Security and Dependency Health, 2 to 6 Weeks#

This phase reduces the risk of being blocked by future upgrades.

For Next.js and React:

  • Upgrade Node.js to an active LTS and align Next.js and React versions.
  • Replace abandoned libraries, especially auth, routing, and form libs if unmaintained.
  • Add security headers and enforce server-side authorization checks.

For Flutter:

  • Migrate to a modern Flutter and Dart baseline supported by your plugins.
  • Replace deprecated plugins and remove unmaintained platform wrappers.
  • Standardize secure storage and network stack.

Deliverable: “supported stack” status plus a dependency upgrade playbook.

Phase 2: Performance and UX Improvements, 2 to 8 Weeks#

This phase is where teams see visible customer impact.

For Next.js and React:

  • Split bundles by route and remove unused dependencies.
  • Fix waterfall requests and caching gaps.
  • Introduce image optimization and predictable SSR and SSG rules.

For Flutter:

  • Remove expensive rebuild patterns and isolate widgets.
  • Optimize lists, images, and JSON parsing.
  • Reduce startup work and lazy-load heavy features.

Deliverable: measured improvements, for example “LCP improved from 3.8 seconds to 2.3 seconds” or “cold start reduced by 35 percent”.

Phase 3: Architecture Modernization, Ongoing#

Do this last, after you can safely change things.

Examples:

  • Introduce a feature-based module structure in React and Next.js.
  • Standardize state management and API boundaries.
  • Extract shared domain logic and add contract tests.
  • For Flutter, enforce clean architecture boundaries where it matters, not everywhere.

Deliverable: reduced lead time per feature and lower defect rate.

💡 Tip: Use a “strangler” approach: modernize one flow at a time behind stable interfaces. This keeps risk local and avoids the multi-month rewrite trap.

# How to Estimate Cost and Timeline From Audit Data#

Leadership needs rough order-of-magnitude estimates, not perfect numbers.

A Practical Estimation Method#

  1. 1
    Group findings into epics by phase.
  2. 2
    For each epic, estimate in “small, medium, large” tied to weeks.
  3. 3
    Add a coordination factor if multiple repos or teams are involved.

Example sizing model:

SizeTypical timeSuitable for
Small0.5 to 2 daysconfig, lint, small refactors
Medium3 to 7 dayslibrary replacements, new test suite
Large2 to 6 weeksmajor upgrades, architecture boundaries

If your due diligence is for outsourcing or handover, also include a transition plan. This reduces the “unknown unknowns” that often inflate delivery timelines after takeover. For a practical framework, see Outsourcing Web Development Guide.

You will move faster in future audits if you standardize templates.

Artifacts to Produce#

ArtifactFormatWho uses it
System mapone page diagramengineering and product
Risk registertable with scoresleadership and security
Dependency upgrade planmilestones by versionengineering
Performance baselinemetrics snapshotengineering and marketing
Roadmapphases with datesleadership

A solid risk register row should include:

  • Finding summary
  • Impact and likelihood
  • Evidence, including file paths or metrics
  • Recommendation
  • Effort estimate
  • Owner and phase

# Common Pitfalls in Legacy App Due Diligence#

  1. 1
    No runtime validation — reviewing code without running flows misses real bottlenecks and crashes.
  2. 2
    Using only static scanners — tools find symptoms, not systemic causes.
  3. 3
    Treating modernization as a rewrite — you lose momentum and usually ship later with the same unknown risks.
  4. 4
    Ignoring observability — without logs and metrics, you cannot prove improvements or catch regressions.
  5. 5
    Skipping stakeholder readout — if leadership does not understand risk, the backlog will not get funded.

# Key Takeaways#

  • Time-box your legacy code audit web mobile to 5 to 10 days and require reproducible builds as the first gate.
  • Score risk with a weighted rubric so security, reliability, and dependency health are not drowned out by subjective opinions.
  • Tie performance findings to measurable baselines like Core Web Vitals for web and startup and jank metrics for Flutter.
  • Convert findings into phases: stabilize first, then security and dependencies, then performance, then architecture modernization.
  • Deliver a roadmap with estimates and acceptance criteria, or the audit will not translate into funded work.

# Conclusion#

Technical due diligence is the fastest way to turn a legacy Next.js, React, or Flutter app from “risky and expensive” into “predictable and upgradeable”. A structured audit plus a scored risk register gives leadership clarity, and a phased modernization plan avoids the rewrite trap while still delivering measurable improvements.

If you want Samioda to run a legacy code audit and deliver a modernization roadmap your team can execute, contact us with repo access and a staging build. We’ll return a scored report, a prioritized backlog, and a phased plan aligned with your release schedule.

FAQ

Share
A
Adrijan OmićevićFounder & Senior Developer

Founder & Senior Developer at Samioda. 8+ years building React, Next.js, Flutter and n8n automation solutions for clients across Europe.

Need help with your project?

We build custom solutions using the technologies discussed in this article. Senior team, fixed prices.