Anti-AI Code Slop
Identify and eliminate the 10 most common AI over-engineering patterns that inflate code complexity without adding value.
@api/anti-ai-code-slop
Anti-AI Code Slop
Identify and eliminate the 10 most common AI over-engineering patterns that inflate code complexity without adding value.
Overview
AI coding assistants produce characteristic over-engineering patterns: defensive code for impossible scenarios, unnecessary abstractions, self-evident comments, and premature generalization. This skill trains you to recognize and eliminate AI code slop before it ships.
Inspired by community research on AI-generated code anti-patterns.
The 10 AI Code Slop Patterns
Pattern 1: Self-Evident Comments
Comments that describe exactly what the code already says.
// Bad — AI slop
const userId = user.id // Get the user's ID
const total = price * quantity // Calculate total by multiplying price by quantity
users.push(newUser) // Add the new user to the users array
// Good — no comment needed, or comment explains WHY not WHAT
const userId = user.id
Rule: Only comment when the code cannot be made self-explanatory, or when you need to explain why a non-obvious choice was made.
Pattern 2: Defensive Error Handling for Impossible Scenarios
Catching errors that cannot occur given the system's invariants.
// Bad — AI slop
function getUser(users: User[], id: string): User {
const user = users.find(u => u.id === id)
if (!user) {
throw new Error(`User not found: ${id}`) // caller guarantees id is valid
}
return user
}
// Good — trust internal invariants
function getUser(users: User[], id: string): User {
return users.find(u => u.id === id)!
}
Rule: Validate at system boundaries (user input, external APIs). Trust internal code and framework guarantees. Don't add error handling for scenarios that can't happen.
Pattern 3: One-Time Abstractions
A helper function or wrapper that is called exactly once and wraps a single operation that was already simple.
// Bad — AI slop
function formatUserDisplayName(user: User): string {
return `${user.firstName} ${user.lastName}`
}
// ...called once, 3 lines away from the definition
// Good — just inline it
const displayName = `${user.firstName} ${user.lastName}`
Rule: Three similar calls justify an abstraction. One call does not. A helper wrapping a single stdlib call adds indirection without removing repetition.
Pattern 4: Backwards-Compatibility Shims for Unshipped Code
Type aliases, re-exports, or renamed variables kept for "compatibility" when the original was never in production.
// Bad — AI slop (this code was never shipped anywhere)
export type UserRecord = User // backwards compat alias
export { getUserById as fetchUser } // old name kept for migration
const _unusedConfig = config // _prefix instead of deletion
// Good — delete unused code
Rule: If the code was never in production and has no external callers, there is nothing to be backward-compatible with. Delete it.
Pattern 5: Feature Flags for Internal Decisions
Using boolean flags or configuration variables for decisions that are purely internal and will never vary at runtime.
// Bad — AI slop
const USE_NEW_VALIDATION = true // feature flag that has been true since day one
function validate(data: unknown) {
if (USE_NEW_VALIDATION) {
return newValidate(data)
}
return oldValidate(data) // never reached
}
// Good
function validate(data: unknown) {
return newValidate(data)
}
Rule: Feature flags are for rollout management. Internal code paths that will never be toggled in production should just be the code.
Pattern 6: Premature Options-Object Generalization
Converting a function with 2 specific parameters to an options object "for future flexibility" before any second usage exists.
// Bad — AI slop
interface CreateUserOptions {
name: string
email: string
role?: string // added "just in case"
sendEmail?: boolean // not used yet
}
function createUser(options: CreateUserOptions) { ... }
// Good — simple parameters for simple calls
function createUser(name: string, email: string) { ... }
Rule: Generalize when you have 3+ callers with varying needs. Two parameters does not need an options object.
Pattern 7: Redundant JSDoc on Self-Evident Functions
Documentation comments that restate what the function signature already communicates.
// Bad — AI slop
/**
* Gets the user by ID.
* @param id - The user's ID
* @returns The user object
*/
function getUserById(id: string): User { ... }
// Good — no docs needed; the name and types explain it
function getUserById(id: string): User { ... }
Rule: Document why something non-obvious is done, or the contract when it's not obvious from types. Don't document "this function does what its name says."
Pattern 8: Over-Typed Single-Use Structures
Defining an interface or type alias for a value that is created once and used once in the same function.
// Bad — AI slop
interface ValidationResult {
isValid: boolean
errors: string[]
}
function validate(input: string): ValidationResult {
const result: ValidationResult = { isValid: true, errors: [] }
// ...
return result
}
// Good — inline type is fine here, or just return the object
function validate(input: string): { isValid: boolean; errors: string[] } {
return { isValid: true, errors: [] }
}
Rule: Named types earn their place when used in 3+ places, in public APIs, or when naming the concept aids readability. Single-use internal structures don't need names.
Pattern 9: Unnecessary Async Wrapping
Wrapping synchronous operations in async/await or Promise.resolve() with no actual async work.
// Bad — AI slop
async function getConfig(): Promise<Config> {
const config = JSON.parse(fs.readFileSync('config.json', 'utf-8')) // sync!
return config
}
// Or worse:
async function add(a: number, b: number): Promise<number> {
return a + b
}
// Good
function getConfig(): Config {
return JSON.parse(fs.readFileSync('config.json', 'utf-8'))
}
Rule: Use async only when the function actually awaits something. Don't future-proof synchronous functions with async wrappers "just in case."
Pattern 10: Significance-Inflating Variable Names
Naming variables with grandeur that inflates the importance of routine operations.
// Bad — AI slop
const comprehensiveUserDataFetchResult = await getUser(id)
const criticalAuthenticationToken = generateToken(user)
const transformedAndNormalizedInputData = input.trim()
// Good
const user = await getUser(id)
const token = generateToken(user)
const trimmed = input.trim()
Rule: Variable names describe what they hold, not how important it is. "result," "user," "token" are fine names.
Review Checklist
Run before every code review or self-review:
[ ] Every comment explains WHY, not WHAT
[ ] No error handling for scenarios that cannot occur
[ ] No function called exactly once that wraps a single line
[ ] No type aliases or re-exports for code that was never shipped
[ ] No feature flags that are always one value
[ ] No options objects for functions with 1-2 natural parameters
[ ] No JSDoc on self-evident function signatures
[ ] No named interfaces used in only one place
[ ] No async functions without actual await calls
[ ] Variable names describe content, not importance
Application Scope
Apply to: code you are writing or reviewing for production use.
Do not apply to:
- Public APIs where backwards compatibility is a real concern
- Libraries where the options object pattern is conventional
- Code that genuinely needs future extensibility (well-established extension points)
- Existing code bases where consistency with surrounding patterns matters more than purity
$20 more to next tier
Created by
Info
Demo
Embed
Add this skill card to any webpage.
<iframe src="https://skillslap.com/skill/2d1296e1-16a9-4d97-b75a-9ca3e752720c/embed"
width="400" height="200"
style="border:none;border-radius:12px;"
title="SkillSlap Skill: Anti-AI Code Slop">
</iframe>