Skip to main content

Overview

Deslop removes AI-generated code bloat from your branch before committing. It checks the diff against main and cleans up unnecessary comments, defensive code, premature abstractions, and over-engineering that AI agents tend to add.
“Three similar lines of code is better than a premature abstraction.” — Clean up the slop.

Trigger

Use when:
  • After completing changes, before committing
  • Code feels over-engineered or has AI smell
  • Before code review or PR
  • Cleaning up after exploratory development
/deslop
/pro-workflow:deslop

Workflow

1

Run Diff

See all changes on the branch
git fetch origin main
git diff origin/main...HEAD --stat
git diff origin/main...HEAD
2

Identify Slop

Find patterns from the focus areas (see below)
3

Apply Minimal Edits

Make focused, minimal edits to remove slop
4

Verify

Re-run diff to verify only slop was removed
git diff origin/main...HEAD
5

Test

Confirm behavior unchanged
npm test -- --changed --passWithNoTests 2>&1 | tail -10
6

Summarize

What was cleaned (1-3 sentences)

Focus Areas

Deslop identifies and removes these patterns:
Comments that state what the code already saysRemove:
// Increment the counter
counter++;

// Return the user object
return user;

// Loop through items
for (const item of items) {
Keep:
// Hash password with bcrypt (rounds=10) to prevent timing attacks
const hash = await bcrypt.hash(password, 10);

// Use XOR cipher to match legacy API format (remove after v2 migration)
const encrypted = xorEncrypt(data);
Try/catch blocks for trusted internal code pathsRemove:
// In internal helper function
try {
  const result = JSON.parse(jsonString);
  return result;
} catch (error) {
  console.error('Parse failed:', error);
  return null;
}
Keep:
// For external API calls or user input
try {
  const response = await fetch(externalApi);
  return await response.json();
} catch (error) {
  logger.error('External API failed', error);
  throw new ExternalServiceError(error);
}
Using any to bypass type issues instead of fixing themRemove:
const data = response as any;
const value = (input as any).someProperty;
Fix:
const data = response as UserResponse;
const value = isInputWithProperty(input) ? input.someProperty : undefined;
Helpers, factories, or abstractions used only onceRemove:
// Used only once
function createUserValidator() {
  return (user: User) => {
    return user.email && user.name;
  };
}

const validator = createUserValidator();
if (!validator(user)) {
  throw new Error('Invalid user');
}
Inline:
if (!user.email || !user.name) {
  throw new Error('Invalid user');
}
Nested conditions that should use early returnsRemove:
function processUser(user: User | null) {
  if (user) {
    if (user.isActive) {
      if (user.email) {
        return sendEmail(user.email);
      }
    }
  }
  return null;
}
Flatten:
function processUser(user: User | null) {
  if (!user) return null;
  if (!user.isActive) return null;
  if (!user.email) return null;
  return sendEmail(user.email);
}
Renamed vars, re-exports, “removed” commentsRemove:
// Legacy export for backwards compatibility
export const oldFunctionName = newFunctionName;

// Renamed but keeping old reference
const _deprecatedVar = newVar;

// // Removed old implementation
// function oldWay() { ... }
Clean:
// Just the new implementation
export const newFunctionName = ...;
Features, refactoring, or improvements beyond what was requestedRemove:
# Original task: Add login endpoint

+ feat: add login endpoint
+ feat: add password reset endpoint  # Not requested
+ refactor: extract auth helpers      # Not requested
+ docs: add API documentation         # Not requested
If you added unrequested features, either:
  1. Remove them (preferred)
  2. Commit separately with justification
Docstrings, type annotations, or comments on code that wasn’t changedRemove:
// File: src/utils/old-code.ts
// You only changed one function, but added docs to all functions

- function existingFunction() { ... }  // Unchanged
+ /**
+  * Existing function that does X
+  * @returns Y
+  */
+ function existingFunction() { ... }  // Still unchanged
Error handling for scenarios that can’t happen in trusted codeRemove:
// In internal function with typed parameters
function add(a: number, b: number): number {
  if (typeof a !== 'number' || typeof b !== 'number') {
    throw new Error('Parameters must be numbers');
  }
  return a + b;
}
Keep:
// Simple and trusts the type system
function add(a: number, b: number): number {
  return a + b;
}

Guardrails

Critical rules when deslopping:

Keep Behavior Unchanged

Unless fixing a clear bug, behavior must stay the same. Don’t “improve” while cleaning.

Minimal Edits

Prefer focused edits over broad rewrites. Remove slop, don’t refactor.

Three Lines > Abstraction

Three similar lines is better than a premature abstraction. Inline one-use helpers.

Verify Unused Before Removing

If removing code, grep to verify it’s truly unused first.

Output

Deslop provides:
  1. List of slop patterns found with file locations
  2. Edits applied to remove slop
  3. One-line summary of what was cleaned
DESLOP REPORT
=============

Slop patterns found (5 instances):

1. Obvious comments (3 files)
   - src/api/users.ts:12 "// Return user object"
   - src/api/users.ts:34 "// Loop through items"
   - src/utils/format.ts:8 "// Convert to string"

2. Premature abstraction (1 file)
   - src/utils/validators.ts:15 createValidator() used only once
   
3. Any cast (1 file)
   - src/api/auth.ts:42 "response as any"

Applying minimal edits...

✓ Removed 3 obvious comments
✓ Inlined createValidator() (1 use)
✓ Fixed type cast to UserResponse

Verifying changes...
git diff origin/main...HEAD --stat
  src/api/users.ts    | 2 --
  src/api/auth.ts     | 2 +-
  src/utils/format.ts | 1 -
  src/utils/validators.ts | 8 +-------
  4 files changed, 2 insertions(+), 11 deletions(-)

Running tests...
✓ Tests: 23 passed

=============
Cleaned: Removed obvious comments, inlined one-use validator, fixed any cast.

Examples

Example 1: Obvious Comments

User: "Clean up this code before I commit"

Claude: Checking for slop in branch diff...

Found: 5 obvious comments

Before:
```typescript
// Get the user from database
const user = await db.users.findOne({ id });

// Check if user exists
if (!user) {
  throw new Error('User not found');
}

// Return the user object
return user;
After:
const user = await db.users.findOne({ id });
if (!user) {
  throw new Error('User not found');
}
return user;
Removed 3 obvious comments. Tests passing.

### Example 2: Premature Abstraction

```bash
User: "This feels over-engineered"

Claude: Checking for premature abstractions...

Found: Helper function used only once

Before (src/api/users.ts):
```typescript
function createEmailValidator() {
  return (email: string) => {
    const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return regex.test(email);
  };
}

const validateEmail = createEmailValidator();

if (!validateEmail(user.email)) {
  throw new Error('Invalid email');
}
After:
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

if (!emailRegex.test(user.email)) {
  throw new Error('Invalid email');
}
Removed factory function (used only once). Tests passing.

### Example 3: Defensive Code

```bash
User: "Remove unnecessary error handling"

Claude: Checking for defensive try/catch in internal code...

Found: 2 unnecessary try/catch blocks

Before (src/utils/format.ts - internal helper):
```typescript
function formatDate(date: Date): string {
  try {
    return date.toISOString();
  } catch (error) {
    console.error('Date format failed:', error);
    return '';
  }
}
This is an internal helper with typed input. The try/catch is unnecessary. After:
function formatDate(date: Date): string {
  return date.toISOString();
}
Removed defensive try/catch from internal helpers. Tests passing.

### Example 4: Nested Code

```bash
User: "Flatten this nested logic"

Claude: Simplifying nested conditions with early returns...

Before:
```typescript
function processOrder(order: Order | null) {
  if (order) {
    if (order.isPaid) {
      if (order.items.length > 0) {
        if (order.shippingAddress) {
          return shipOrder(order);
        } else {
          throw new Error('No shipping address');
        }
      } else {
        throw new Error('No items');
      }
    } else {
      throw new Error('Order not paid');
    }
  } else {
    throw new Error('No order');
  }
}
After:
function processOrder(order: Order | null) {
  if (!order) throw new Error('No order');
  if (!order.isPaid) throw new Error('Order not paid');
  if (order.items.length === 0) throw new Error('No items');
  if (!order.shippingAddress) throw new Error('No shipping address');
  
  return shipOrder(order);
}
Flattened nested conditions. Logic unchanged. Tests passing.

## Integration with Pro Workflow

<CardGroup cols={2}>
  <Card title="Smart Commit" icon="code-commit" href="/skills/smart-commit">
    Run deslop before smart-commit for clean commits
  </Card>
  <Card title="Wrap-Up" icon="check-circle" href="/skills/wrap-up">
    Wrap-up can trigger deslop during quality check
  </Card>
  <Card title="Orchestrate" icon="diagram-project" href="/skills/orchestrate">
    Review phase includes deslop before committing
  </Card>
  <Card title="Learn Rule" icon="graduation-cap" href="/skills/learn-rule">
    Capture patterns: "Don't add obvious comments"
  </Card>
</CardGroup>

## Configuration

### Auto-Deslop Before Commit

Add to CLAUDE.md:

```markdown
## Before Commits

1. Run /deslop to remove AI slop
2. Run quality gates
3. Commit with /smart-commit

Custom Slop Patterns

Add project-specific patterns to detect:
## Deslop Patterns

Also remove:
- Logger statements in pure functions
- Timezone conversions (we use UTC only)
- Null checks for non-nullable types

Best Practices

AI agents tend to add defensive code and obvious comments. Clean up after each session.
Reviewers will thank you for removing the bloat before they see it.
Always run tests after deslop. Ensure behavior unchanged.
Remove slop, don’t refactor. Save refactoring for a dedicated task.

What to Keep

Not all AI-generated code is slop. Keep:
// Use XOR to match legacy API (remove after v2 migration)
// Hash with rounds=10 to prevent timing attacks
// HACK: Workaround for Safari bug #12345
try {
  const response = await fetch(externalApi);
  return response.json();
} catch (error) {
  logger.error('External API failed', error);
  throw new ServiceUnavailableError();
}
if (!email || typeof email !== 'string') {
  throw new ValidationError('Email required');
}
// Used in 5 places
function validateUser(user: User) {
  if (!user.email || !user.name) {
    throw new ValidationError('Invalid user');
  }
}

Troubleshooting

Tests Fail After Deslop

If tests fail after cleaning:
  1. Revert the last deslop edit
  2. Investigate why the “slop” was actually needed
  3. Add a comment explaining why (non-obvious case)

Too Aggressive

If deslop removes code that should stay:
  1. Review the diff carefully before committing
  2. Restore removed code with comment explaining why needed
  3. Update deslop patterns to skip this case

Not Finding Slop

If deslop reports no issues but code feels bloated:
  1. Manually review the diff for patterns not in the list
  2. Use git diff origin/main...HEAD to check yourself
  3. Add new patterns to your CLAUDE.md deslop config

Next Steps

Try Smart Commit

Commit clean code with quality gates

Learn Pro Workflow

Master the complete workflow system

Master Wrap-Up

Include deslop in your end-of-session ritual

View All Skills

Explore the complete skill system

Build docs developers (and LLMs) love