← All field notes
Vibe Coding

AI Slop Code: How to Tell if Your Codebase Has It (and What to Do)

AI slop code is the new technical debt. Here are 8 specific patterns I find in audits, the r/cscareerquestions thread that named the problem, and what to do if your codebase has it.

Matthew TurleyMay 28, 202610 min read

Six days ago a thread on r/cscareerquestions titled something like "engineers have stopped thinking" hit 2,061 upvotes. The top comments were senior developers describing pull requests from junior devs and contractors that read like they were written by someone who had never thought about the problem, only about getting the AI to emit something that compiled. Long, plausible, structurally confident, and load-bearing nothing.

That is AI slop. Not bad code per se. Code that looks like it understands the problem and does not. Code that would fail a 5-minute conversation about why it was written that way, because the writer cannot answer the question.

I use AI codegen every day. Claude Code, Cursor, the lot. So this is not "AI bad, humans good". This is "the failure mode is real, here is what it looks like, here is how to tell if your codebase has it, and here is what to do." If you built your app yourself with Lovable or Cursor or v0, you are inside the population at risk by definition. If you hired a developer who delivered fast and you cannot quite say why something feels off when you read the code, you may be inside it too.

Here are the eight patterns I see most often in audits.

1. The triple-fallback

The code tries a thing, catches the error, tries a different thing, catches that error, then returns a hardcoded default. None of the catches log anything. The author never decided which of the three paths was actually the right one, so they shipped all three. In production, the third path is the one that always runs, because the first two were never quite right, and nobody noticed because the default value is plausible enough.

The smell: a function has three try blocks in a row and the final catch returns null or [] or "". The fix is almost always to delete two of the three branches.

2. The defensive coat of paint

Every function checks if every argument is null, undefined, an empty string, an empty array, and the wrong type, even when the function is called from exactly one place where the argument can only be a non-empty string. The checks are noise. The author did not know who would call the function or when, so they hedged against every possible input. The hedges hide the actual contract.

The smell: a 30-line function where the first 18 lines are guard clauses against impossible inputs. The fix is to delete the guards and add a type annotation.

3. Comments that paraphrase the next line

Every line of code has a comment above it that re-states what the line does in English. // Set the user variable to the user object above const user = userObject. The AI generates these because the prompt asked for "well-documented code". They are not documentation. They are noise that hides the lines that genuinely need a comment, like the one explaining why a particular timeout value is 4.7 seconds.

The smell: comment-to-code ratio over 1:3 throughout the codebase, with the comments being descriptions rather than explanations. The fix is to delete every comment that paraphrases its line, and write one good comment per file explaining the part you would forget in 6 months.

4. Reinventing a library function that exists three feet away

A 40-line helper function that deep-clones an object, with edge cases for Dates and Maps, while the codebase already imports Lodash. Or a custom debounce written from scratch in a React component while useDebouncedValue is imported one file over. The AI did not look at the existing code first.

The smell: utility functions in random files that duplicate something already in the project's utils/ directory or in a dependency. The fix is to grep for the function before writing it, every time. (This is also the smell that hardest distinguishes AI-assisted code from AI slop. The good AI sessions notice the existing helper. The slop sessions reinvent.)

5. The shotgun import

A single React component imports 14 things from 9 different files. Eight of those imports are unused. Two are wrong (they imported Button from @radix-ui/themes when the project uses Button from a local design system). The AI generated the imports from a template and never reconciled them with what the file actually uses.

The smell: unused imports show up in lint warnings nobody addresses. Or worse, two different Button components rendered side by side on the same page. The fix is eslint --fix plus a manual sweep for "duplicate component" bugs.

6. Tests that test the mock

There is a test file. It is 300 lines long. Every test mocks every dependency, then asserts that the mocks were called with the arguments the test passed in. None of the assertions touch the real behavior of the function. The test would pass if the function did literally nothing useful, as long as it called the mocks in the expected order.

The smell: 100 percent test coverage on a function with a known bug. The fix is to write one test that calls the function with real inputs and asserts on the real output, then delete the mocking ceremony.

7. The phantom feature

A configuration option, a feature flag, or a settings toggle that is read from somewhere, defaults to something, and is never set to anything else anywhere in the codebase. The user-facing UI for changing it does not exist. The AI added it because "real apps have configuration" but never followed through with the wiring.

The smell: grep for the flag name, find one read site and one default, no write sites. The fix is to delete the flag and inline the default value, or to actually ship the configuration UI.

8. The narrative variable name

Variables named theResultOfTheUserLookupAfterValidation, correctlyFilteredAndSortedListOfActiveSubscribers, temporaryHolderForTheIntermediateState. The names are sentences. They are sentences because the AI was prompted to make the code self-documenting and concluded that the way to do that was to put the documentation in the variable name. The result is unreadable because every line is 140 characters of variable names and the actual operation is invisible.

The smell: average variable name length over 25 characters. The fix is to rename to short names and put the explanation in a one-line comment if needed.

How to tell if your codebase has the problem

You do not need to be technical to run this assessment. Open three random files in your codebase. Read the first 50 lines of each. Then ask yourself, or your developer:

  • Of the code in front of me, what fraction would I delete without changing what the app does? If the answer is more than 20 percent, you have slop.
  • Pick one function and ask "why does this function exist and what calls it". If the developer (or you, if you wrote it with AI) cannot answer in one sentence, that function is slop.
  • Open the test file for that function. Does the test exercise the real function, or does it exercise mocks? If it is mocks all the way down, the tests are slop.

The fastest tell, in my experience, is the comments. A codebase with one good comment per file is usually fine. A codebase with a comment above every line is almost always AI slop. The author was not thinking about which lines deserved explanation. They were generating "documented code" by the yard.

Why this matters for a small founder team

The cost of slop is not that the app does not work. The app usually works on the happy path. The cost is the second-order failure mode: nobody can change the code safely, because nobody can predict what will break. Every new feature takes longer than the last one. Bugs come back because the fix was applied in only one of the three try-fallback branches, and the fallback is what runs in production. The team (or you) starts hesitating before every PR because the code does not feel reliable, and hesitation costs more than slop.

For a solo founder with one customer, this is fine for a while. For a solo founder with 50 paying customers, this is the moment you realize you cannot ship the feature your biggest customer asked for, because every time you touch the auth code, billing breaks in a different way.

What to do if you find slop

Three options, in increasing order of effort.

Option one: delete first, replace second. Find a slop pattern. Delete it. See if anything broke. Most of the time, nothing breaks, because slop tends to be inert. The defensive checks, the unused imports, the phantom flags, the triple-fallback branches that never fire. You can usually remove 10 to 20 percent of an AI-built codebase in an afternoon with no behavior change. This is also the fastest way to make the codebase legible enough to reason about.

Option two: rewrite the worst file. Pick the one file you dread touching. The auth file, usually. Rewrite it from scratch with a clear mental model of what it should do. Throw out the old one. If you have AI in the loop, drive it with a clearer prompt this time, one that says "here is the existing schema, here is the existing user flow, write the simplest implementation". Review every line.

Option three: paid audit. If you cannot tell which of your files are slop and which are fine, a fresh pair of senior eyes will sort it in a few hours. The Continuum Vibe Audit is $349 for a written report sorted by priority. I will tell you which files are load-bearing, which are slop, and which are the launch-blockers from the pre-launch checklist. If the issue is that the original developer shipped and disappeared and you cannot read the code at all, that is a Rescue, not an audit.

One thing the Reddit thread got right and one it got wrong

The "engineers stopped thinking" thread was right that the failure mode is real, common, and rising. AI codegen is making it cheap to ship code without thinking about it, and a lot of people are doing exactly that.

The thread was wrong, in my reading, that the answer is to use less AI. The answer is to use AI with a stronger floor under it. A reviewer (human or another model) who asks "why" of every change. A test suite that exercises the real function, not the mocks. A reading habit where you read every line the AI generated before you merge it, and delete the lines that exist only to make the AI sound confident.

I ship AI-assisted code every day and I do not consider any of mine slop. The difference is not the tool. It is the second pass.

Book a Vibe Audit if you want a second pass on your code. Or book a free 30-minute call if you want to talk through whether your situation needs an audit, a rescue, or just a sharper prompt for tomorrow's session.

M
Matthew Turley, Continuum

Fractional CTO and embedded technical partner. 20+ years shipping production software.

Try a First PR Sprint, $299 →