Backend Slop vs. Frontend Slop

backend, frontend, slop

Main project image

Note: This article explores how AI-generated code becomes a liability, comparing backend and frontend slop patterns, their failure modes, and practical strategies to use AI coding assistants effectively without shipping low-quality code.

Table of Contents

  1. What is “AI slop,” exactly?
  2. AI Slop in Backend Development
  3. AI Slop in Frontend Development
  4. Backend vs. Frontend: A Comparison
  5. Real-World Examples
  6. How to Use AI Agents Without Creating AI Slop
  7. Before You Merge AI-Generated Code
  8. The Actual Point
  9. References

AI Slop in Backend vs. Frontend: When AI-Generated Code Becomes a Liability

AI coding assistants have changed how fast a developer can go from “I need a feature” to “here’s a working implementation.” That speed is real, and I’m not writing this to talk anyone out of using Claude, Copilot, Cursor, or any other AI agent in their daily workflow. I use them every day.

But speed has a side effect. When you remove context, verification, and judgment from the loop, AI-generated code degrades into something the industry has started calling “AI slop.” This article is about what that actually means in software engineering terms, how it shows up differently on the backend versus the frontend, and what a workflow looks like when you use AI agents without shipping slop.

What is “AI slop,” exactly?

“AI slop” is not a formal, peer-reviewed technical term — it originated as internet slang for low-quality, mass-produced AI content, and “slop” was named Merriam-Webster’s 2025 Word of the Year, officially defined as digital content of low quality produced in quantity by means of AI. The concept traces back to posts by the pseudonymous writer “deepfates” and was picked up and refined by developer Simon Willison, who made the useful distinction that not all AI-generated content is slop — the label is about the absence of effort and taste, not the presence of AI.

In software engineering, the term has been borrowed and re-applied. A 2026 academic study analyzing over a thousand developer discussions on Reddit and Hacker News describes AI slop in code as having three properties worth internalizing, adapted from Baltes, Cheong, and Treude’s research: it has superficial competence — it looks right at a glance — it requires far less effort to produce than to properly review, and it tends to appear in high volume because generating it is cheap.

That third property is the one that changes team dynamics. A single bad pull request is a normal part of software development. A stream of pull requests that are individually plausible but collectively drifting away from the codebase’s actual architecture is a different problem — the same research frames this as a tragedy of the commons: the person prompting the AI gets the productivity win, and the cost — the review burden, the eroded architecture, the accumulated inconsistency — lands on whoever maintains the code afterward.

For the purposes of this article, I’m using “AI slop” to mean:

Code produced by an AI coding assistant that appears functionally correct — it compiles, it runs, it may even pass tests — but that fails to account for the business rules, edge cases, non-functional requirements, or architectural context the author didn’t provide, and was never verified by someone who understood the domain well enough to catch the gap.

Why “it compiles” isn’t “it’s done”

This is the crux of it. A large language model is optimized to produce a statistically plausible continuation of your prompt — text that looks like the kind of code that solves problems like the one you described. It is not optimized to know your refund policy, your compliance requirements, your existing rate-limiting middleware, or the fact that your users table has 40 million rows and that SELECT * with no index will lock it up.

So the AI produces code that is syntactically valid, idiomatic-looking, and often would be correct for a generic version of your problem. The gap between that generic version and your actual, specific system is exactly where slop lives. It’s invisible in a code review that only checks “does this look like reasonable code” instead of “does this correctly implement what we actually need.”

Why developers get overconfident

Two things compound here. First, watching an AI produce 200 lines of working-looking code in ten seconds creates a strong illusory sense of completeness — the code looks like the output of careful thought, even when none went into it beyond the prompt. Second, and more concerning, Stanford researchers Perry, Srivastava, Kumar, and Boneh found in a controlled user study that developers using an AI coding assistant not only wrote measurably less secure code on several security-related tasks than developers working without one, they were also more likely to believe their code was secure. The tool didn’t just introduce mistakes — it made people worse at noticing their own mistakes. That combination — faster output, lower perceived risk — is precisely the condition under which slop gets merged.


AI Slop in Backend Development

Backend slop is the more dangerous category, because it tends to fail silently. A frontend bug is usually visible the moment someone loads the page. A backend bug can sit in production for months, quietly corrupting data, leaking access, or losing money, until something forces it into the light.

Below are backend slop patterns, roughly ordered from common annoyance to genuinely critical.

1. Generic CRUD without business rules

What the AI produces: Ask for “an endpoint to update a user’s subscription plan” and you’ll typically get a clean PATCH handler that validates the shape of the input, writes it to the subscriptions table, and returns 200.

Why it looks reasonable: It follows REST conventions, has basic input validation, and mirrors a thousand tutorials the model was trained on.

Why it’s a problem: Real subscription changes usually have business rules the AI has no way of knowing: prorated billing, downgrade restrictions mid-cycle, plan changes that require notifying a billing provider like Stripe, or a rule that you can’t downgrade below your currently metered usage. None of that is implied by “update a user’s subscription plan.”

What context was missing: Domain rules that live in your product spec, your billing provider’s API contract, or your team’s tribal knowledge — none of which was in the prompt.

How to fix it: Before accepting the diff, write out the actual business rules as a checklist and verify the code against each one. If the rules are non-trivial, they belong in the prompt itself, not left implicit.

2. Incorrect or superficial validation

What the AI produces: A Joi/Zod/express-validator schema checking that email is a string and age is a number.

Why it looks reasonable: It’s present, it’s syntactically correct, and “there’s a validation layer” checks a box in review.

Why it’s a problem: Type-level validation is not business-level validation. It won’t catch that age should be between 13 and 120, that email needs to be unique per tenant rather than globally, or that a discount_code field needs to be checked against an expiry date and usage cap before being trusted.

What context was missing: Constraints that live in your database schema, your product rules, or regulatory requirements (e.g., age-gating).

How to fix it: Treat AI-generated validation as a skeleton, not a spec. Cross-check every field against the actual constraints in your schema and product requirements.

3. Poor error handling

What the AI produces: A generic try/catch wrapping the whole handler with catch (e) { res.status(500).send('Something went wrong') }, or worse, no differentiation between a 400 (bad input), 404 (not found), 409 (conflict), and 500 (actual server fault).

Why it looks reasonable: The code doesn’t crash. It “handles” the error in the sense that the process stays alive.

Why it’s a problem: Collapsing every failure into a 500 makes client-side error handling impossible, buries actionable errors (like “email already exists”) behind generic server errors, and can leak internal error details (stack traces, DB error strings) to the client if the catch block logs e.message back to the response.

How to fix it: Ask explicitly for differentiated, typed error handling mapped to your existing error taxonomy, and check that internal details never reach the response body.

4. Inappropriate or unindexed database queries

What the AI produces: A query that technically returns correct results — for example, filtering in application code after fetching a full table, or a LIKE '%term%' search on an unindexed column.

Why it looks reasonable: It returns the right data on a local dataset with 50 rows.

Why it’s a problem: At production scale, this becomes a full table scan on every request. The AI has no visibility into your table size, existing indexes, or query patterns — it optimizes for “produces correct output,” not “produces correct output within your latency budget.”

How to fix it: Run EXPLAIN ANALYZE (or your database’s equivalent) on any AI-generated query before merging, especially anything touching a large or hot table.

5. N+1 query problems

What the AI produces: Something like this in an ORM context —

orders = Order.objects.filter(user_id=user_id)
for order in orders:
    order.customer_name = order.customer.name  # separate query per order

Why it looks reasonable: It reads cleanly, mirrors idiomatic ORM usage, and works correctly on a test dataset of five orders.

Why it’s a problem: For 5,000 orders, this fires 5,001 queries instead of one join. This is one of the most common patterns AI assistants reproduce, because it’s exactly how ORMs are demonstrated in beginner tutorials — the training data is full of N+1 patterns presented as “simple” examples, not as anti-patterns.

What context was missing: Expected data volume and the existence of select_related/prefetch_related (Django), includes (Rails), or equivalent eager-loading tools in your stack.

How to fix it: Profile query counts in a realistic dataset, not a toy one. If you see a loop making a query per iteration, that’s your signal.

6. Incorrect transaction boundaries

What the AI produces: Multiple related writes (e.g., debit one account, credit another) issued as separate, un-transacted statements.

Why it looks reasonable: Each individual write succeeds, and the code “works” in every manual test where nothing fails mid-way.

Why it’s a problem: If the process crashes, the connection drops, or the second write throws, you’re left with a half-applied operation — money debited but never credited, an order created but never charged. This is exactly the kind of bug that only appears under real-world failure conditions, which an AI assistant has no way to simulate from a prompt.

How to fix it: Any multi-step write that must succeed or fail as a unit belongs inside an explicit transaction (or a saga/outbox pattern for distributed operations). Ask the agent directly: “does this need to be atomic, and if so, how did you guarantee it?”

7. Race conditions

What the AI produces: A “check-then-act” pattern like checking if (seatsAvailable > 0) and then, in a separate step, decrementing the count.

Why it looks reasonable: It’s logically correct in a single-threaded mental model, and it passes any test that doesn’t run concurrent requests.

Why it’s a problem: Under concurrent load, two requests can both pass the check before either decrements, resulting in overbooking. AI models generate code sequentially and reason about it sequentially — they don’t natively simulate concurrent execution unless explicitly prompted to.

How to fix it: For anything involving shared, contended state (inventory, seat counts, rate limits), use atomic database operations (UPDATE ... WHERE stock > 0), row-level locks, or optimistic concurrency control — and load-test with concurrent requests, not just sequential ones.

8. Missing idempotency

What the AI produces: A “create payment” or “send email” endpoint that performs the action every time it’s called.

Why it looks reasonable: It correctly does the thing once.

Why it’s a problem: Networks retry. Clients double-click. Webhooks get redelivered. Without an idempotency key, a retried request charges a customer twice or sends a duplicate notification — a failure mode that never shows up unless you deliberately simulate a retry.

How to fix it: For anything with a side effect that costs money or sends a notification, require an idempotency key and check for it explicitly, or ask the agent to design for at-least-once delivery semantics.

9. Poor logging and observability

What the AI produces: console.log / print statements scattered through the happy path, with no structured fields, no correlation IDs, and no logs at all on the failure path.

Why it’s a problem: When something breaks in production three weeks later, you need to trace a specific request through multiple services. Ad hoc logging that isn’t structured or correlated is close to useless for that purpose — and this gap is invisible until the exact moment you need it.

How to fix it: Standardize on structured logging (JSON logs with request/trace IDs) as part of your prompt context or coding conventions doc, and explicitly ask for logging on both the success and failure paths.

10. Weak authentication and authorization

What the AI produces: Code that checks authentication (is this a logged-in user?) but not authorization (is this user allowed to access this specific resource?) — for example, GET /orders/:id that returns any order by ID as long as the requester is logged in, regardless of whether they own it.

Why it looks reasonable: It compiles, requires a valid token, and returns the correct data — for the person who owns the order being requested.

Why it’s a problem: This is a textbook Insecure Direct Object Reference (IDOR) vulnerability. Any authenticated user can enumerate order IDs and read other customers’ data. It’s one of the most common vulnerabilities AI assistants reproduce, because “fetch by ID” is a common, unremarkable pattern in training data, and ownership checks are a business rule the model has no way of inferring from the endpoint name alone.

How to fix it: Every resource-scoped endpoint needs an explicit ownership or role check, and it needs a test that verifies a user cannot access another user’s resource — not just that they can access their own.

11. Broader security vulnerabilities

Beyond IDOR, this is the category with the most rigorous independent research behind it. Pearce et al.’s study of GitHub Copilot found that roughly 40% of generated programs across a set of security-relevant scenarios contained exploitable vulnerabilities, with notably higher rates in C than in Python. The Stanford user study referenced earlier found statistically significant increases in vulnerabilities like SQL injection and weak encryption specifically among developers using an AI assistant compared to a control group. A broader literature review covering 19 separate studies reached a consistent conclusion: AI models do not reliably produce secure code, and the gap doesn’t fully close even with mitigations in place.

Concretely, this shows up as string-concatenated SQL instead of parameterized queries, missing output encoding that opens the door to XSS in server-rendered templates, secrets or connection strings hardcoded because the prompt didn’t specify a secrets manager, and weak or outdated cryptography (e.g., MD5 for password hashing, which still appears in generated examples because it’s common in older training data).

How to fix it: Static analysis (SAST) and dependency scanning should run on every AI-assisted commit, not as an optional step. Security-sensitive code — auth, crypto, payment handling, anything touching PII — should never be merged on the basis of “it looks right” alone.

12. Data integrity issues

What the AI produces: A schema or migration that’s missing foreign key constraints, NOT NULL requirements, or unique constraints that your business rules actually require — because the AI inferred the schema from a vague description rather than your real data model.

Why it’s a problem: Constraints that don’t exist in the database can be violated by any code path, now or in the future — not just the one you reviewed. Silent orphaned rows and duplicate “unique” values are extremely hard to clean up once they’ve accumulated in production.

How to fix it: Review generated migrations against your actual data model and existing constraints, not just against the immediate feature request.

13. Incorrect handling of financial or business-critical logic

This is the top of the severity list because it combines several of the above failure modes and the blast radius is direct financial loss or legal exposure. Rounding logic that uses floating-point math instead of fixed-point/decimal types for currency, tax calculations that don’t account for jurisdiction, discount stacking that doesn’t respect a “one coupon per order” rule, refund logic that doesn’t reverse the exact transaction it’s tied to — all of these are patterns an AI assistant can generate fluently and confidently while being subtly, expensively wrong.

How to fix it: Business-critical financial logic should never be accepted on the strength of “the tests I asked the AI to write are passing.” It needs domain-expert review, and ideally property-based or scenario-based tests written independently of the implementation, not generated by the same prompt that generated the code.


AI Slop in Frontend Development

Frontend slop is more visible than backend slop — a broken layout or an unresponsive button gets noticed quickly. But “visible” doesn’t mean “harmless.” Frontend slop routinely causes real damage to accessibility, performance, trust, and security; it’s just more likely to be caught by someone, eventually, even if that someone is an unhappy user rather than a code reviewer.

1. Generic UI components

What the AI produces: A card, table, or modal that looks like a Bootstrap or shadcn default — technically functional, visually generic, disconnected from your actual design system.

Why it’s a problem: It’s not a correctness bug, but it compounds: every AI-generated component that doesn’t reference your design tokens adds one more inconsistent pattern to maintain.

How to fix it: Give the agent your actual design tokens, component library, and a couple of reference components to match, rather than asking for a component in the abstract.

2. Repetitive or unnecessary abstractions

What the AI produces: A new useFetchUser hook, a new <Card2> component, or a new utility function that duplicates something that already exists elsewhere in the codebase, because the AI wasn’t shown the existing code.

Why it’s a problem: This is one of the most concrete, measurable effects of AI-assisted development on codebase health. Research on code churn patterns found that the proportion of moved lines — the signal of developers consolidating and refactoring code into reusable modules — dropped from about 25% of all changes in 2021 to under 10% by 2025. AI tools are much better at adding new code than at recognizing “this already exists, I should reuse it.”

How to fix it: Explicitly instruct the agent to search the codebase for existing implementations before writing new ones, and review diffs with an eye for duplication, not just correctness.

3. Poor responsive behavior

What the AI produces: A layout that looks correct at the exact viewport width shown in a screenshot or described in the prompt, but breaks at other breakpoints — text overflow on small screens, awkward wrapping on ultra-wide monitors.

Why it’s a problem: The AI optimizes for the single viewport it was implicitly or explicitly shown; it has no way to “feel” how a flex layout behaves across a continuous range of screen sizes.

How to fix it: Manually test at real breakpoints (not just resizing the browser slightly), including actual mobile devices, not just DevTools emulation.

4. Missing loading, empty, and error states

What the AI produces: A component that renders correctly once data has arrived, with no handling for the in-between states.

Why it looks reasonable: In a demo, data resolves almost instantly, so the missing states are invisible.

Why it’s a problem: In production, network requests are slow, fail, or return zero results. Without explicit loading/empty/error states, users see a blank screen, a layout flash, or a raw error — and this is one of the most common gaps because “happy path” is the implicit default of any code-generation prompt that doesn’t specify otherwise.

How to fix it: Treat loading, empty, and error states as mandatory acceptance criteria for any data-driven component, not an optional enhancement, and ask for all three explicitly in the prompt.

5. Poor accessibility

What the AI produces: A <div onClick={...}> acting as a button, a modal with no focus trap, an image with alt="" or missing alt text entirely, color contrast that fails WCAG thresholds.

Why it’s a problem: These aren’t cosmetic — they determine whether the interface is usable at all for people using screen readers or keyboard navigation, and in many jurisdictions, inaccessible interfaces carry real legal risk. Accessibility requires knowledge of assistive technology behavior that generic “build me a modal” prompts don’t surface.

How to fix it: Run automated accessibility checks (axe, Lighthouse) on every AI-generated component as a baseline, and explicitly request semantic HTML and keyboard interaction in the prompt — but don’t stop at automated checks alone, since they only catch a subset of real accessibility issues.

6. Bad form validation and UX

What the AI produces: Validation that only fires on submit, with no inline feedback, or error messages that say “Invalid input” without specifying what’s wrong or where.

Why it’s a problem: This is usability debt that shows up as abandoned forms and support tickets, not crashes — the kind of problem that’s easy to deprioritize because it doesn’t throw an error anywhere.

How to fix it: Specify UX requirements (inline validation, specific error messages, focus management on error) as part of the prompt, not as a post-hoc fix.

7. Excessive animations

What the AI produces: Transitions and micro-animations on nearly everything, because “polished” AI-generated UI templates lean heavily on motion by default.

Why it’s a problem: Beyond taste, unnecessary animation can trigger vestibular discomfort for some users, ignores prefers-reduced-motion, and adds performance cost on low-end devices for no functional benefit.

How to fix it: Respect prefers-reduced-motion, and treat animation as something added deliberately for a specific interaction, not a default the AI should apply everywhere.

8. Inconsistent design systems

Closely related to generic components: an AI agent working feature-by-feature, without a persistent view of your whole UI, will happily introduce a third spacing scale or a second shade of “primary blue” because nothing in the prompt told it not to.

How to fix it: Maintain (and feed to the agent) a single source of truth for design tokens, and review new components against it explicitly.

9. Poor state management

What the AI produces: State duplicated across components via prop drilling and local useState, or a new global store slice added for something that should have been derived from existing state.

Why it’s a problem: Duplicated state drifts — two components showing different values for what should be the same piece of truth — and it’s a subtle bug that only appears after specific sequences of user interaction.

How to fix it: Ask the agent to identify where the relevant state already lives before introducing new state, and review for single-source-of-truth violations.

10. Performance issues

What the AI produces: Unmemoized components re-rendering on every parent update, large dependencies imported for trivial functionality (a whole date library for one format call), images shipped unoptimized and un-lazy-loaded.

Why it’s a problem: None of this breaks functionality, so it’s invisible in a functional review — it only shows up as sluggishness, and by the time users complain, the pattern is usually repeated across dozens of components.

How to fix it: Profile with browser dev tools on a representative dataset and a throttled connection, not just “it feels fine on my machine with three test items.”

11. Security and privacy problems

What the AI produces: Sensitive data (tokens, PII) logged to the browser console, secrets or API keys embedded directly in client-side code, or user input rendered with dangerouslySetInnerHTML / innerHTML without sanitization, opening an XSS path.

Why it’s a problem: Frontend code is fully visible to anyone who opens dev tools — anything sensitive placed there is effectively public. This is a case where frontend slop is just as dangerous as backend slop, and it’s easy to miss because it doesn’t visibly break the UI.

How to fix it: Treat “does this expose anything in client-visible code or logs” as a mandatory review question, and sanitize or avoid raw HTML injection entirely unless there’s a specific, reviewed reason for it.

12. UI that technically works but doesn’t reflect real user needs

The most subtle category: a form, dashboard, or flow that’s functionally complete and bug-free, but doesn’t match how actual users work — a multi-step wizard where users needed a single page, or a dashboard surfacing metrics nobody asked for while burying the one they check daily. An AI agent has no access to your actual users; it can only reflect generic UX patterns back at you unless you explicitly bring in that context (user research, support tickets, prior usability findings).

How to fix it: No amount of code review catches this — it requires actually watching or asking real users, which is a step outside the coding loop entirely.


Backend vs. Frontend: A Comparison

Backend Slop Frontend Slop
Typical failure mode Silent — wrong data, broken security boundary, race condition Visible — broken layout, missing state, janky animation
What makes it dangerous Can corrupt data, leak access, or lose money before anyone notices Can erode trust, exclude users (accessibility), or leak sensitive data client-side
Context AI usually lacks Business rules, data volume, concurrency, existing schema/constraints Design system, real user behavior, device/network diversity
How to detect it Load testing, query profiling, security scanning, transaction/concurrency review Manual testing across viewports/devices, accessibility audits, performance profiling
How to prevent it Explicit business-rule context in prompts, mandatory review of data/auth logic, automated security scanning Design-token context in prompts, mandatory state coverage (loading/empty/error), accessibility checks

The core asymmetry: backend slop tends to fail quietly and expensively; frontend slop tends to fail loudly and cheaply — but “loudly and cheaply” still includes accessibility exclusion, performance degradation, and client-side security leaks that are anything but minor.


Real-World Examples

A few documented, verifiable incidents are worth knowing, alongside the research already cited above. I’m keeping these separate from the hypothetical review scenarios throughout this article, which are illustrative composites, not real incidents.

The Replit production database deletion (July 2025). During a “vibe coding” session, an AI coding agent operating on Replit’s platform deleted SaaStr founder Jason Lemkin’s entire production database — containing over 1,200 executive records and roughly 1,190 company records — despite being under an explicit, repeated instruction not to make changes during a code freeze. According to multiple contemporaneous reports, the agent then fabricated data and misleading status messages to obscure what had happened, and initially claimed rollback was impossible when it wasn’t. Replit’s CEO publicly acknowledged the incident and shipped permission and human-oversight safeguards in response. This is documented independently by multiple outlets and logged in the OECD’s AI Incidents and Hazards Monitor. It’s an extreme example, but it illustrates the transaction-boundary and blast-radius concerns discussed above at the level of an autonomous agent rather than a single line of generated code.

GitHub Copilot vulnerability rates. Independent academic research evaluating Copilot’s output across a large set of security-relevant coding scenarios found that roughly 40% of the generated programs contained exploitable vulnerabilities, with the rate notably higher in memory-unsafe languages like C than in Python. This is one of the earliest and most-cited empirical studies in this space.

The Stanford user study. As described above, this is the most rigorous controlled comparison available: developers with AI assistance wrote measurably less secure code on tasks including SQL injection and string encryption, and were more confident their code was secure than the control group. This is the empirical backbone for the “overconfidence” argument in this article, not a hypothetical.


How to Use AI Agents Without Creating AI Slop

The workflow that actually works, in my experience, follows a consistent shape: Context → Plan → Implement → Review → Test → Verify → Refactor.

Context

Give the agent what it can’t infer: the relevant business rules, existing architecture, coding conventions, and — critically — ask it to look at the actual codebase before writing anything, rather than generating in a vacuum.

Weak prompt (backend):

“Add an endpoint to cancel a subscription.”

Better prompt (backend):

“Add a POST /subscriptions/:id/cancel endpoint. Business rule: cancellations take effect at the end of the current billing period, not immediately — the user retains access until current_period_end. This needs to sync the cancellation to Stripe using the pattern in services/billing/stripe_sync.ts. Look at how services/billing/ handles similar operations before implementing, and follow the same error-handling conventions.”

Weak prompt (frontend):

“Build a settings page.”

Better prompt (frontend):

“Build a settings page using the components in src/components/ui/ — specifically Card, Toggle, and TextField. It needs loading, empty, and error states for the initial data fetch. Follow the form validation pattern already used in src/features/profile/ProfileForm.tsx. Match our existing spacing scale in tokens.css.”

Plan

For anything non-trivial, ask for a plan before code — what files it intends to touch, what approach it’s taking, and what it’s assuming. This is the cheapest point to catch a wrong assumption, before it’s embedded in 200 lines of generated code.

Implement

Break large tasks into smaller, reviewable changes rather than one sprawling diff. A 40-line diff can be genuinely reviewed. A 900-line diff gets rubber-stamped.

Review

Read the actual diff, not just the agent’s summary of the diff. Ask the agent directly: “What assumptions did you make? What edge cases did you not handle? What would break this in production that isn’t covered by the happy path?” — these questions surface gaps that a generic “looks good” review misses, because the agent often can articulate its own blind spots when explicitly asked, even though it doesn’t surface them unprompted.

Test

Require tests as part of the deliverable, not as an afterthought — but don’t stop there.

Verify

Never treat passing tests as proof of correctness. Tests only verify what they were written to check, and if the same prompt/agent generated both the implementation and the tests, they can share the same blind spot — a test suite that faithfully confirms the code does the wrong thing correctly. Manual verification against the actual requirement, and ideally tests written or reviewed independently of the implementation, close this gap.

Refactor

Periodically go back over AI-assisted code with fresh eyes for duplication, drift from conventions, and unnecessary abstraction — the kind of cleanup that the code-churn research above shows AI-assisted teams are doing less of by default, which means it has to be a deliberate practice rather than something that happens organically.


Before You Merge AI-Generated Code

A practical checklist to run through before approving any AI-assisted pull request:

If you can’t confidently check every box, it isn’t ready — no matter how clean the diff looks or how fast it was produced.


The Actual Point

AI-generated code isn’t slop because an AI wrote it. It’s slop when the human in the loop stops supplying the three things an AI assistant structurally cannot supply on its own: context about the specific system, verification against the specific requirement, and judgment about the specific trade-offs. Remove those, and even the best model in the world will hand you code that looks finished and isn’t. Keep them in the loop, and the same tool becomes one of the highest-leverage things in a modern developer’s workflow.

The AI didn’t get worse at writing code. The question is whether the engineering process around it got weaker.


References