We built Audit Vibe Coding to audit AI-generated projects across security, performance, SEO, and code quality. Over time we catalogued a set of signals that reliably distinguish vibecoded output from hand-written code.
Here are the patterns that come up most often and the edge cases that make detection harder than it sounds.
1. Uniform formatting with zero local quirks
Human codebases accumulate formatting drift. One file uses single quotes, another uses double. Tabs in the config, spaces in components. AI-generated code is eerily consistent because it comes from one session or a handful of sessions with the same model.
When we see a 40-file React project where every component follows the exact same structure (imports, type definition, component, export default) with zero deviation, that's a signal. Real teams argue about formatting. AI does not.
The false positive: a team using strict Prettier plus ESLint will also look uniform. We cross-check against the presence of linter config files. If .prettierrc exists with non-default settings, we discount this signal.
2. Verbose-but-generic variable names
AI models lean toward descriptive names: handleUserProfileUpdate, isAuthenticationLoading, fetchDashboardDataFromAPI. Each name reads like documentation. Human code tends to abbreviate after the third time typing something: updateProfile, authLoading, getDashData.
More telling: AI rarely uses single-letter loop variables or abbreviations that require project context to understand. You won't see const tx = buildTxPayload() in vibecoded output because the model optimizes for clarity over speed.
3. Boilerplate density
AI happily generates full CRUD scaffolds, complete error boundary wrappers, and elaborate auth flows. The ratio of boilerplate to business logic skews high. A 200-line component where 140 lines are loading states, error states, and type guards around 60 lines of actual logic is typical.
We measure this as a ratio. When boilerplate-to-logic exceeds roughly 3:1 across multiple files, it suggests generation rather than writing. A human would extract the repeated pattern into a shared hook or utility after the second copy.
4. Missing edge case handling in non-obvious spots
This is one of the more reliable signals. AI handles the cases it was prompted about and produces clean happy-path code. But probe the edges:
// AI-typical: handles the obvious null case
if (!user) return null;
// But misses: what if user.preferences is an empty object
// vs undefined vs null? What about stale cache?
const theme = user.preferences.theme || 'light';
Hand-written code in a production codebase accumulates defensive checks in weird places because someone hit that bug on a Tuesday night. AI code is clean in a way that suggests nobody has ever run it under load.
5. Import patterns: everything included, not everything used
AI imports tend to be comprehensive. The model generates the import block for the whole file at once, and sometimes the final code path doesn't use every import. ESLint or TypeScript strict mode catches this, but if you audit the git history and the very first commit already has clean imports with zero unused-import warnings, that's suspicious. Real first commits are messy.
The subtler version: AI imports from the "correct" package according to documentation, even when the project already has a wrapper or alias. You'll see import { useState } from 'react' next to a custom useAppState hook that the rest of the codebase uses.
6. Comment patterns
AI comments explain what the code does, line by line. Human comments explain why:
// AI pattern:
// Check if user is authenticated and redirect if not
if (!isAuth) redirect('/login');
// Human pattern:
// Safari private mode clears session on tab switch,
// so we check auth state on every route change (see #247)
if (!isAuth) redirect('/login');
The second version references a bug number, a browser quirk, context that lives outside the code. AI comments are correct but contextless.
7. Security anti-patterns hidden under good structure
This one matters most for our audit work. AI-generated code often looks well-structured. Components are separated, routes are organized, types are defined. But security gaps hide underneath:
Inline API keys in environment files that get committed. CORS set to * in development config that ships to production. Input validation on the frontend with nothing on the backend. Rate limiting absent entirely.
The pattern is that structural quality is high (the code looks professional) while security depth is shallow (nobody has threat-modeled it). We see this on roughly 70% of the vibecoded projects that come through Audit Vibe Coding.
8. Error handling: catch-all or nothing
AI tends toward two extremes. Either every async call gets a proper try/catch with typed error responses, or errors pass through silently. What you rarely see is the messy middle ground of real production code: partial error handling where critical paths are wrapped and low-risk paths aren't, retry logic in some places but not others, and custom error classes that grew organically.
What doesn't work
Some signals sound good in theory but fail in practice:
File naming conventions are unreliable. Both AI and senior devs use kebab-case or PascalCase consistently. Commit history helps but only if you have access. And code quality metrics (cyclomatic complexity, line count) overlap too much between good AI output and good human code to be useful alone.
Detection works best as a weighted combination. No single signal is conclusive. But when 4 or 5 of the above patterns show up together, the probability climbs fast.
Why this matters
At Inithouse, we ship a portfolio of products and we use AI tools ourselves. Knowing whether a codebase is vibecoded changes the audit approach. AI-generated code needs different scrutiny than hand-written code: less focus on stylistic issues, more focus on the gaps that models systematically miss.
If you're shipping an AI-built project and want a scored report covering security, SEO, performance, and code quality, Audit Vibe Coding runs the full check without requiring an account.
Jakub, builder @ Inithouse
Top comments (0)