Engineering Management ·

Technical Due Diligence for AI-Generated Codebases: Assessment Frameworks, Risk Scoring, and Remediation Roadmaps for Investors and CTOs

A systematic framework for evaluating codebases partially or fully generated by AI tools, including security audit methodology, architecture review, risk scoring, and realistic remediation timelines.

Technical Due Diligence for AI-Generated Codebases: Assessment Frameworks, Risk Scoring, and Remediation Roadmaps for Investors and CTOs

Investors and CTOs are now regularly evaluating companies whose core product was built entirely with Cursor, Claude Code, Copilot, Lovable, or Replit. The codebase works well enough to demo. It has users, possibly revenue. The question is not whether the product idea is sound — the question is whether the technical foundation can carry the weight of what comes next.

Traditional due diligence frameworks were not designed for this scenario. The standard checklist (test coverage, dependency audit, architecture diagram) will miss the specific failure patterns that emerge from AI-assisted development. You will get a green scorecard on a system that is three incidents away from a rebuild.

This guide gives you a structured methodology for evaluating AI-generated codebases honestly: what to look for, how to score risk, and how to build a remediation roadmap with real time and cost estimates.

Why AI-Generated Code Fails Differently

Before walking through the framework, it is worth being precise about what makes AI-generated codebases structurally different from human-written ones.

Human engineers make mistakes that follow recognizable patterns. Debt accumulates where business pressure is highest. Abstractions break where the problem domain was misunderstood. You can usually trace a bad design decision to a specific moment in the project’s history.

AI-generated code fails differently. The issues are not concentrated at architectural decision points — they are distributed uniformly throughout the codebase, because every file was written by a tool optimizing for local correctness, not systemic coherence. The result is a codebase that:

  • Passes surface-level inspection (the code looks reasonable line by line)
  • Contains identical vulnerability patterns repeated across dozens of files
  • Has tests that achieve coverage metrics without providing meaningful safety guarantees
  • Carries dependency bloat from whatever package solved the immediate problem at generation time
  • Lacks the implicit shared understanding that human teams build over time

Veracode’s longitudinal study, now covering 150+ large language models, confirms the security dimension of this: the security pass rate for AI-generated code has remained flat at approximately 55% despite rapid improvements in syntactic correctness. Syntax climbed from 50% to 95% accurate since 2023. Security stayed at 55%. The models learned to write cleaner-looking code that is equally vulnerable.

The Assessment Framework

Structure your evaluation across five layers. Each layer has a different risk profile and requires different tooling. Do not collapse them into a single audit pass — you will miss category-specific failure modes.

Layer 1: Security Audit

This is the highest-urgency layer for any AI-generated codebase. Start here regardless of what the product does.

Run automated scanning first to establish a baseline:

// Example: Automated SAST baseline with Semgrep
// Run against the full repository before any manual review
import { execSync } from "child_process";
import * as fs from "fs";

interface SecurityFinding {
  ruleId: string;
  severity: "ERROR" | "WARNING" | "INFO";
  path: string;
  line: number;
  message: string;
  cwe?: string;
}

interface SecurityBaseline {
  totalFindings: number;
  critical: SecurityFinding[];
  high: SecurityFinding[];
  secretsExposed: number;
  owasp: Record<string, number>;
  scannedAt: string;
}

function runSecurityBaseline(repoPath: string): SecurityBaseline {
  const semgrepOutput = execSync(
    `semgrep --config=p/owasp-top-ten --config=p/secrets --json ${repoPath}`,
    { encoding: "utf-8" }
  );

  const results = JSON.parse(semgrepOutput);

  const findings: SecurityFinding[] = results.results.map((r: any) => ({
    ruleId: r.check_id,
    severity: r.extra.severity.toUpperCase(),
    path: r.path,
    line: r.start.line,
    message: r.extra.message,
    cwe: r.extra.metadata?.cwe,
  }));

  const secretFindings = findings.filter((f) =>
    f.ruleId.includes("secret") || f.ruleId.includes("credential")
  );

  const owaspCategories: Record<string, number> = {};
  findings.forEach((f) => {
    const category = f.cwe?.split(":")[0] ?? "unknown";
    owaspCategories[category] = (owaspCategories[category] ?? 0) + 1;
  });

  return {
    totalFindings: findings.length,
    critical: findings.filter((f) => f.severity === "ERROR"),
    high: findings.filter((f) => f.severity === "WARNING"),
    secretsExposed: secretFindings.length,
    owasp: owaspCategories,
    scannedAt: new Date().toISOString(),
  };
}

Manual review follows the automated pass and targets four specific vulnerability classes that are disproportionately common in AI-generated code:

Missing authorization checks. AI tools generate working API endpoints without always attaching the correct ownership verification. A route that creates a resource will often have authentication (the request must be from a logged-in user) but not authorization (the resource must belong to that user). Check every mutation endpoint.

SQL injection through ORM misuse. The model knows to use an ORM, but will sometimes reach for raw query interpolation when the ORM pattern does not immediately fit. Search for template literals inside database calls.

Hardcoded credentials and exposed secrets. AI models generate example connection strings with real-looking values, and those values sometimes persist into production. The Escape.tech scan of 5,600 AI-generated apps found 400+ exposed secrets in production endpoints.

Inverted access control logic. This one is subtle. The conditional is backwards: if (!isAdmin) { allowAccess() }. The code looks plausible in isolation. The logic is wrong. CVE-2025-48757 documented this pattern across 170 production Lovable-built applications.

Layer 2: Architecture Review

AI tools write code file by file. They do not hold the global system design in mind across sessions. The result is architectural incoherence that does not surface in a code review of any individual file.

Look for these structural failure modes:

Scattered middleware. Authentication, validation, and rate limiting logic appears in multiple layers (route handlers, service functions, database queries) rather than in a single consistent layer. This creates gaps where the logic does not run uniformly, and makes every future change a security question.

Missing abstraction boundaries. Business logic lives in route handlers. Database queries appear in React components. The system cannot be tested in isolation because nothing is isolated.

N+1 query patterns at scale. AI-generated data access code works correctly for small datasets but does not account for how query counts grow with data volume. A product with 50 users has no performance problem. At 5,000 users, the dashboard takes 40 seconds to load because it runs 5,000 individual queries.

No error propagation strategy. Errors are caught at the point where they occur and either silently swallowed or logged without propagating structured information. The application fails without producing actionable diagnostics.

// Common pattern in AI-generated codebases: errors swallowed silently
async function getUserProfile(userId: string) {
  try {
    const user = await db.users.findUnique({ where: { id: userId } });
    return user;
  } catch (error) {
    // AI generated this catch block to satisfy the linter
    console.log(error);
    return null;  // calling code has no idea something failed
  }
}

// What the production-ready version looks like:
class UserNotFoundError extends Error {
  constructor(public userId: string) {
    super(`User ${userId} not found`);
    this.name = "UserNotFoundError";
  }
}

async function getUserProfileSafe(
  userId: string
): Promise<{ id: string; email: string }> {
  const user = await db.users.findUnique({ where: { id: userId } });

  if (!user) {
    throw new UserNotFoundError(userId);
  }

  return user;
}

Layer 3: Test Coverage Analysis

Test coverage percentage from AI-generated codebases is actively misleading. The tests were written to satisfy the coverage requirement, not to catch bugs.

Look past the percentage at what the tests actually assert:

  • Tests that create a mock, call a function, and assert the mock was called. These verify that the code calls what it calls, not that it does anything correct.
  • Tests that test the happy path exclusively and never exercise error conditions, boundary values, or invalid inputs.
  • Integration tests that hit a fake database and therefore cannot catch schema mismatches, query performance problems, or constraint violations.
  • No tests for the security-critical paths: authorization checks, input validation, token handling.

A codebase with 85% line coverage and no tests for auth logic is less tested than a codebase with 40% coverage that specifically targets authorization boundaries.

Layer 4: Dependency Audit

AI tools select packages based on training data frequency, not production suitability. The result is dependency trees with common failure patterns:

// Run this audit script and review each category of finding
import { execSync } from "child_process";

interface DependencyRisk {
  name: string;
  version: string;
  riskType: "outdated" | "vulnerability" | "unused" | "duplicate" | "abandoned";
  severity: "low" | "medium" | "high" | "critical";
  detail: string;
}

function auditDependencies(packageJsonPath: string): DependencyRisk[] {
  const risks: DependencyRisk[] = [];

  // npm audit for known vulnerabilities
  const auditOutput = execSync("npm audit --json", { encoding: "utf-8" });
  const audit = JSON.parse(auditOutput);

  for (const [name, advisory] of Object.entries(audit.vulnerabilities as any)) {
    const vuln = advisory as any;
    risks.push({
      name,
      version: vuln.range,
      riskType: "vulnerability",
      severity: vuln.severity,
      detail: vuln.via?.[0]?.title ?? "Known vulnerability",
    });
  }

  // Check for packages last published more than 2 years ago
  // (signals abandoned maintenance)
  const packageJson = JSON.parse(
    require("fs").readFileSync(packageJsonPath, "utf-8")
  );
  const allDeps = {
    ...packageJson.dependencies,
    ...packageJson.devDependencies,
  };

  // Count total packages: >150 production deps is a signal worth flagging
  const prodDepCount = Object.keys(packageJson.dependencies ?? {}).length;
  if (prodDepCount > 150) {
    risks.push({
      name: "package.json",
      version: "all",
      riskType: "unused",
      severity: "medium",
      detail: `${prodDepCount} production dependencies. Manual review for package bloat required.`,
    });
  }

  return risks;
}

Specific things to flag: multiple packages solving the same problem (three different date libraries, two HTTP clients), packages installed as production dependencies that are build-time tools, and packages with zero dependents that were added to solve a one-time problem and never removed.

Layer 5: Maintainability Scoring

The final layer assesses the engineering cost to make changes going forward. This matters most for investors evaluating whether the team can execute the roadmap without a full rebuild.

Score each of the following on a 0-2 scale (0 = absent, 1 = partial, 2 = adequate):

  • Local development environment setup time (target: under 15 minutes for a new engineer)
  • Environment parity between local and production
  • Database migration strategy (are migrations tracked in version control and applied automatically?)
  • Deployment pipeline (is there CI/CD, or are deployments manual?)
  • Observability (structured logging, error tracking, basic performance monitoring)
  • Documentation of non-obvious decisions

Risk Scoring Methodology

Once you have completed all five layers, aggregate findings into a single risk score. This is the number that tells you what category of intervention is needed.

LayerWeightMax PointsScoring Inputs
Security35%35Critical findings (−10 each), secrets exposed (−15), OWASP coverage
Architecture25%25Coherence, error handling, query strategy
Test Coverage15%15Effective coverage quality, not raw percentage
Dependencies15%15CVE count, bloat level, abandoned packages
Maintainability10%10Setup time, deploy pipeline, observability

Score interpretation:

  • 80-100: Codebase is in reasonable shape. Targeted improvements only.
  • 60-79: Significant issues but manageable. A 6-12 week stabilization sprint before scaling.
  • 40-59: High risk. Plan for 3-6 months of hardening work before any major feature development.
  • Below 40: Rebuild conversation is likely warranted. The cost of stabilizing may exceed the cost of a fresh implementation with the correct architecture.

Most AI-generated codebases evaluated in the wild land between 45-65 on this scale.

Common Failure Patterns: A Quick Reference

PatternHow to Spot ItRisk LevelTypical Fix Cost
Scattered auth middlewaregrep for auth/token checks outside the middleware layerCritical2-4 weeks
False-confidence testsCoverage >80% but no auth or error path testsHigh3-6 weeks to rewrite test suite
Swallowed errorscatch (e) { return null } throughout codebaseHigh2-3 weeks systematic pass
Package bloat>150 prod deps, duplicate utility librariesMedium1-2 weeks audit and trim
N+1 queriesNo include or join on list endpointsHigh1-3 weeks per affected domain
No migration strategySchema changes applied by hand in productionHigh1-2 weeks to introduce tooling
Inverted access controlBoolean logic on ownership checksCritical1 week to audit, varies to fix
Hardcoded secretsCredentials in source or .env committed to repoCriticalImmediate rotation + 1-2 days cleanup

Building the Remediation Roadmap

Translate findings into a phased plan. Do not present all issues at once — prioritize by risk category and sequence work to unlock business goals.

Phase 1: Stop the bleeding (weeks 1-3). Address all Critical-severity findings first: rotate exposed secrets immediately, patch inverted access control bugs, add authorization checks to unprotected endpoints. This phase has no scope negotiation. If there are exposed secrets in production, that work starts today.

Phase 2: Foundation hardening (weeks 4-12). Systematic error handling pass, consolidate middleware, introduce migration tooling, establish CI/CD if absent, add observability. These are the changes that make the codebase safe for a development team to work in.

Phase 3: Quality layer (weeks 12-24). Rebuild the test suite targeting meaningful coverage rather than line coverage metrics. Address architectural coherence issues (extracting business logic from route handlers, removing duplicated abstractions). Trim dependency bloat.

Phase 4: Scale preparation (weeks 24+). Address N+1 patterns, introduce caching where appropriate, load test against realistic traffic projections.

Budget calibration for a typical seed-stage AI-generated codebase:

PhaseDurationEngineering Cost (estimate)
Stop the bleeding1-3 weeks$8,000 - $18,000
Foundation hardening4-12 weeks$30,000 - $80,000
Quality layer12-24 weeks$40,000 - $100,000
Scale preparationOngoing$15,000 - $40,000/quarter

These ranges assume senior engineers who can both audit and build. Junior engineers working under senior supervision add calendar time without proportionally reducing cost.

Production Considerations

A few operational notes that affect how you scope the assessment itself:

Assess before making commitments. The risk score should inform the acquisition price or engagement contract, not follow from it. A 60-point codebase priced as if it were 85 is a future dispute.

Treat test coverage percentage as a lagging indicator. The first thing to do with any test suite on an AI-generated codebase is read the tests, not run them. A 90% coverage badge on tests that only assert function calls happened is worse than 40% coverage on tests that exercise real logic.

Version control history tells a story. A repository with 50 commits where 45 of them are “fix bug” or “update” without meaningful descriptions indicates the development process was exploratory and reactive. Expect more undocumented decisions and surprise behavior.

The bus factor is often zero. In AI-generated codebases, no single human holds a coherent mental model of the full system. The original developer worked with the AI tool to generate each piece, but the system-level understanding that comes from writing every line yourself never formed. This is not a criticism of the founder — it is a structural consequence of how the code was produced. It means every senior engineer who joins will face the same orientation cost.

Security issues compound. A single exposed secret that granted access to a database also means that database’s contents may already be in the wrong hands. Treat any secret exposure as a breach until demonstrated otherwise.

The point of this framework is not to render a verdict. It is to replace intuition with structure. AI-generated codebases are not automatically bad. Some are surprisingly solid. But the failure modes are specific, the risk distribution is different from what traditional due diligence catches, and the cost of missing a critical finding at the assessment stage is substantially higher than the cost of a thorough audit upfront.

A codebase is a liability before it is an asset. Measure it accordingly.

More in Engineering Management

The AI Productivity Paradox: Why Your Team Ships More Code but Delivers Less
Engineering Management ·

The AI Productivity Paradox: Why Your Team Ships More Code but Delivers Less

AI coding tools create an illusion of velocity at the individual level while degrading team-level delivery, quality, and maintainability. The core mechanism is a 5x+ senior/junior productivity split that aggregate metrics hide entirely.

The AI Productivity Paradox: Why Your Team Ships More Code but Delivers Less Value
Engineering Management ·

The AI Productivity Paradox: Why Your Team Ships More Code but Delivers Less Value

93% of developers use AI coding tools, yet DORA metrics haven't improved proportionally. Individual output rises while bug rates, review times, and deployment instability climb. Here is why individual AI productivity gains create organizational drag, and how to fix it with architecture-level guardrails.

Why Your Engineering Team Is Shipping Slower Than 6 Months Ago
Engineering Management ·

Why Your Engineering Team Is Shipping Slower Than 6 Months Ago

Engineering velocity declines at seed-to-Series-A startups for predictable, diagnosable reasons. Process debt, unclear ownership, hiring mistakes, burnout, and architectural bottlenecks all compound. Here is a diagnostic framework you can run in one afternoon, plus a tradeoffs table for each intervention.

The AI Ratchet Effect: Why Giving Your Engineering Team AI Tools Made Them Work Harder, Not Smarter
Engineering Management ·

The AI Ratchet Effect: Why Giving Your Engineering Team AI Tools Made Them Work Harder, Not Smarter

67% of engineers who adopted AI tools in 2025 worked more hours by year-end, not fewer. This is the AI ratchet effect: management converts every productivity gain into a permanently higher baseline. Here is how it happens, why it is worse at startups, and what a sustainable AI adoption cadence actually looks like.