Variant Systems

February 26, 2026 · Variant Systems

We Audited 5 Claude Code Projects. Here's What We Found.

Real findings from auditing Claude Code-built SaaS apps. The code is clean — the problems are underneath. Common patterns, severity breakdown, and what to fix first.

claude-code code-audit ai-generated-code security startup

We audit codebases for founders, investors, and teams inheriting code they didn’t write. Over the past three months, five of those audits have been Claude Code projects — SaaS apps built primarily or entirely with Claude Code.

The pattern is consistent enough that it’s worth sharing publicly. Not because Claude Code is bad. It’s genuinely the best AI coding tool we’ve worked with. The code quality is high, the patterns are consistent, and the output reads like it was written by a senior engineer.

That’s exactly why the problems are dangerous. They hide under clean code.

Here’s what we found across five real audits, anonymized but otherwise unchanged.

The five projects

ProjectStackSizeStage
A — B2B scheduling SaaSNext.js, Prisma, PostgreSQL~18K LOCPre-seed, 200 beta users
B — AI document processorPython FastAPI, React, S3~12K LOCRaising seed round
C — E-commerce analyticsNext.js, Supabase~22K LOCPost-launch, $8K MRR
D — Healthcare intake formsReact Native, Node.js, MongoDB~15K LOCCompliance review for hospital pilot
E — Internal tool for a logistics companyElixir/Phoenix, LiveView, PostgreSQL~9K LOCPre-deployment review

All five founders were technical enough to prompt well and review output. None of them were shipping blindly. These weren’t vibe-coded toys — they were real products with real users or real contracts pending.

The findings

We run every audit through our automated audit tool first — 7 analyzers covering secrets, security, dependencies, structure, tests, imports, and AI patterns. That catches the structural issues. Then a senior engineer reviews architecture, assumptions, and business logic.

Across five projects, here’s the breakdown:

Critical findings: 14 total

FindingOccurrencesProjects affected
Hardcoded secrets in source4A, B, C, D
SQL injection / NoSQL injection3A, C, D
Missing authentication on API routes3A, B, C
Broken access control (IDOR)2B, D
Sensitive data in client bundle2A, C

4 out of 5 projects had secrets committed to version control. Stripe keys, SendGrid API keys, database connection strings. Not in .env.example files — in actual source code. Claude Code generates working code, and “working” sometimes means hardcoding the value you pasted into the prompt.

3 projects had injectable queries. Claude Code uses ORMs correctly most of the time, but when it drops to raw queries — for complex joins, full-text search, or performance-sensitive paths — it interpolates user input directly. The code looks intentional. It’s not obviously wrong the way a junior developer’s SQL concatenation would be. It’s a well-formatted query with a variable inserted in exactly the wrong place.

3 projects had unauthenticated API routes. Not all routes — just the ones added later in the development process. The auth middleware was set up correctly for the first batch of endpoints. Later endpoints, added through shorter prompts, didn’t inherit the middleware. Claude Code doesn’t remember that your API requires authentication unless you tell it every time.

Warning findings: 31 total

FindingOccurrencesProjects affected
No error monitoring (Sentry, etc.)5All
No structured logging5All
Missing rate limiting4A, B, C, D
No input validation beyond types4A, B, C, E
Test files with weak/no assertions4A, B, C, D
Circular import dependencies3A, C, D
Single-tenant data model for multi-tenant product2A, B
No database migrations strategy2C, D
Over-engineered abstractions3A, B, E
Missing CORS/security headers3B, C, D

Every single project had zero error monitoring and zero structured logging. This is the most consistent finding across all Claude Code audits. The code handles errors — try/catch blocks exist, error responses are returned — but nothing gets recorded anywhere. When something fails in production, nobody knows. Users see a blank screen or a generic error. The founder finds out when someone complains on Twitter.

4 out of 5 had test files that don’t actually test anything meaningful. Claude Code generates test files. They import the right modules, call the right functions, and use the right assertion syntax. But the assertions are shallow — checking that a function returns something rather than checking that it returns the right thing. Tests that always pass are worse than no tests. They give you false confidence that your code works.

// What Claude Code generates
test('createUser returns user', async () => {
  const user = await createUser({ email: 'test@example.com' });
  expect(user).toBeDefined();  // This will literally never fail
});

// What the test should actually check
test('createUser returns user with hashed password', async () => {
  const user = await createUser({ email: 'test@example.com', password: 'secret' });
  expect(user.email).toBe('test@example.com');
  expect(user.password).not.toBe('secret');
  expect(user.id).toMatch(/^usr_/);
  expect(user.createdAt).toBeInstanceOf(Date);
});

The pattern underneath

If you look at those findings as a list, they seem like individual bugs. They’re not. They’re symptoms of one underlying pattern:

Claude Code builds features. It doesn’t build systems.

A feature is “users can sign up.” A system is “users can sign up, and we know when sign-ups fail, and we rate-limit the endpoint, and we validate inputs beyond type checking, and we log the event for analytics, and we handle the edge case where the email already exists with a deleted account.”

Claude Code nails the first part. The feature works. The code is clean. But all the operational, security, and resilience concerns that make a feature production-ready — those are missing unless you explicitly prompt for each one. And even then, the implementation is often minimal.

This isn’t a bug in Claude Code. It’s a fundamental limitation of building from prompts. Prompts describe what you want. Production systems require thinking about what you don’t want — failure modes, attack vectors, data corruption scenarios, scale bottlenecks. Nobody prompts for those things because they’re not thinking about them at build time.

Severity by project stage

The interesting thing is that severity correlates with how far along the project was, not how complex it was.

Project E (the Elixir/Phoenix internal tool) had the fewest issues. It was also the project where the founder had a detailed spec before starting and treated Claude Code as an implementation tool rather than a design partner. They made the architectural decisions. Claude Code wrote the code. The assumption gap was smallest because the assumptions were made by a human.

Project C (the analytics dashboard at $8K MRR) had the most issues. It was also the project that had been built iteratively over months — feature by feature, prompt by prompt. Each new feature compounded assumptions from the previous ones. By the time we audited it, the codebase had three different patterns for API error handling, two competing state management approaches, and an auth system that had been modified four times as requirements evolved.

The lesson: Claude Code is excellent at implementing a known design. It’s dangerous when it’s also making the design decisions.

What we recommend after every Claude Code audit

1. Run the automated scan first

We open-sourced our audit tool as a Claude Code plugin. It catches the structural issues — secrets, security patterns, dependency vulnerabilities, test quality, circular imports. Takes five minutes. Do this before the human review.

npx skills add variant-systems/skills --skill code-audit

2. Audit assumptions, not just code

The code is probably fine. The assumptions underneath it might not be. For every major architectural decision in your codebase — auth model, data architecture, API design, error handling — ask: did I specify this, or did Claude assume it?

Map the assumptions. Check them against your actual business requirements. This is where the real bugs live.

We wrote a detailed guide on what Claude Code gets wrong and how to fix it — it covers the five most common assumption-level problems.

3. Add the operational layer

This is the fastest fix because it’s additive. You’re not rewriting code — you’re adding what’s missing:

  • Error monitoring (Sentry, Highlight, or similar)
  • Structured logging (not console.log)
  • Rate limiting on public endpoints
  • Input validation beyond TypeScript types
  • Security headers (CORS, CSP, HSTS)
  • Health check endpoints
  • Graceful error handling that doesn’t expose stack traces

Claude Code won’t add these unless you ask. And even when you ask, check the implementation against production best practices.

4. Fix auth and data model issues early

If your auth model or data architecture is wrong, fix it now. These are foundations. Everything else is built on them. The cost of changing them goes up exponentially as you add features.

Two of our five audits required significant rework to the data model. Both founders said the same thing: “I wish I’d known this three months ago.” An audit at week two would have caught it.

The cost of not auditing

Across five projects:

  • Project B needed the data model reworked before their seed round. Two weeks of engineering work that delayed the raise by a month.
  • Project D failed the hospital’s initial security review because of the NoSQL injection and missing access controls. Three weeks of fixes, plus a re-review cycle.
  • Project C had a production incident caused by the unauthenticated API routes — a user discovered they could access other accounts’ data by changing an ID in the URL. The founder found out from a customer email.

The cost of an audit is measured in days. The cost of not auditing is measured in lost deals, security incidents, and delayed launches.

Get your codebase checked

If you’ve built with Claude Code — or any AI tool — and you’re approaching a launch, a fundraise, or a compliance review, get the codebase audited while fixing things is still cheap.

Start with our free AI Code Health Check. Five questions, two minutes. You’ll get a risk assessment and a cost estimate for a full audit — no sales call required.

For the automated pass, install our open-source audit plugin and run it yourself. It’s MIT-licensed, zero dependencies, and it catches the 70% of issues that are structurally detectable.

For the other 30% — the architecture review, the assumption audit, the business logic validation — that’s what we do.


Variant Systems is the quality layer for AI-generated code. We audit, rescue, and fix codebases built with every major AI tool.