All skills
asyrafhussin avatar

/code-slop

@346603c

Detect AI-generated code patterns ("slop") in PHP/Laravel and TypeScript/React source — comment narration, generic naming, premature interfaces, defensive overdose, mock-everything tests, and the absence of human "scars". Use when reviewing AI-assisted PRs, auditing code for taste/quality (not metrics — that's technical-debt), or hardening a code-review checklist. Triggers on "review for AI slop", "find AI patterns", "check code feels human", "audit code-quality taste".

Use this Skill: https://skilld.dev/gh/asyrafhussin/agent-skills/code-slop

This session only. Nothing lands on disk.

rulesnaming-suffix-abuse.md

≈1.4k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Suffix Abuse — *Helper, *Manager, *Util, *Wrapper, *Processor

Impact: HIGH (Catch-all suffixes signal undecided design and pad code with empty abstractions)

UserHelper, OrderManager, DataUtil, RequestWrapper, PaymentProcessor — these suffixes are the AI's go-to when it doesn't know what to call something. The suffix is non-information: every class is in some sense a "manager" or "helper" or "util". The name describes the noun-form of "code I had to put somewhere".

When a model generates these, it's because:

  • The "Helper" class is doing things that belong on the model
  • The "Manager" is a procedural blob masquerading as a class
  • The "Util" is a kitchen sink that should be split or absorbed into domain types

Real domain code uses domain names: Pricing, RefundPolicy, WebhookVerifier — not PricingHelper, RefundManager, WebhookProcessor.

Incorrect

// ❌ Suffix abuse — every concern wrapped in *Helper or *Manager

class UserHelper
{
    public function formatName(User $user): string {
        return $user->firstName . ' ' . $user->lastName;
    }
}

class OrderManager
{
    public function processOrder(Order $order): void {
        // ...
    }
}

class PaymentProcessor
{
    public function processPayment(Order $order, string $token): Charge {
        // ...
    }
}

class DataUtil
{
    public static function arrayToCsv(array $rows): string { /* ... */ }
    public static function csvToArray(string $csv): array { /* ... */ }
    public static function snakeToCamel(string $s): string { /* ... */ }
    public static function camelToSnake(string $s): string { /* ... */ }
    // …grows forever
}
// ❌ Same TS pattern
class StringHelper {
  static capitalize(s: string): string { /* ... */ }
  static slugify(s: string): string { /* ... */ }
}

class ResponseWrapper {
  constructor(private res: ApiResponse) {}
  getData() { return this.res.data; }
}

Why it's slop:

  • UserHelper.formatName(user) could just be user.fullName on the User model
  • OrderManager.processOrder(order) is two ways of saying the same thing
  • DataUtil is a kitchen sink — every utility ends up here, none have a clear home
  • The suffix doesn't add information; it admits the author didn't decide what the class is

Correct

// ✅ Behaviour lives on the domain type; helpers split by concern

class User extends Model
{
    public function getFullNameAttribute(): string
    {
        return "{$this->firstName} {$this->lastName}";
    }
}

// Action class — replaces "OrderManager.processOrder"
final class PlaceOrder
{
    public function __invoke(Order $order, PaymentIntent $payment): void { /* ... */ }
}

// Domain class with a clear single responsibility — replaces "PaymentProcessor"
final class StripePaymentGateway implements PaymentGateway
{
    public function charge(Money $amount, string $token): Charge { /* ... */ }
}

// CSV becomes its own type — replaces the kitchen-sink DataUtil
final class CsvExporter
{
    public function export(iterable $rows, array $headers): string { /* ... */ }
}
// ✅ Methods on the domain type, or named domain functions
class User {
  get fullName(): string { return `${this.firstName} ${this.lastName}`; }
}

// Free function — capitalize doesn't need a class wrapper
export function capitalize(s: string): string { /* ... */ }
export function slugify(s: string): string { /* ... */ }

Why it reads human:

  • Each class has a specific domain responsibility (CsvExporter exports CSVs; PlaceOrder places orders)
  • Behaviour-on-data is on the type that holds the data (user.fullName)
  • Free functions stand on their own when there's no state to wrap

When suffixes ARE okay

A handful of suffixes carry real meaning in their conventions:

  • *Repository — DDD-style data access (specific contract)
  • *Service — used sparingly for orchestration that doesn't fit on a domain type
  • *Controller — HTTP entry point (Laravel/Express convention)
  • *Middleware — request pipeline (Express/Laravel convention)
  • *Gateway — external integration boundary (Stripe, AWS)
  • *Listener / *Observer — event-handler conventions (Laravel)

*Helper, *Manager, *Util, *Wrapper, *Processor are the suspect tier.

Detection

# Class declarations with suspect suffixes
grep -rEn 'class\s+[A-Z][a-zA-Z]*(Helper|Manager|Util|Utils|Wrapper|Processor|Handler)\b' \
  --include='*.php' --include='*.ts' --include='*.tsx' app/ src/

# Count by suffix to size the problem
grep -rE 'class\s+[A-Z][a-zA-Z]*' --include='*.php' --include='*.ts' app/ src/ \
  | grep -oE '(Helper|Manager|Util|Utils|Wrapper|Processor)\b' \
  | sort | uniq -c | sort -rn

A repo with 5+ *Helper or 3+ *Manager classes is almost certainly leaking domain logic into catch-all classes. Refactor by asking "what would I call this if I couldn't use the suffix?"

Reference: Clean Code — Chapter 2: Meaningful Names · Internal: over-eng-single-method-class

Source: SKILL.md on GitHub

No alerts16d3 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    The skill provides heuristics for auditing code quality in PHP and TypeScript projects and is generally safe. It contains a low-severity risk of indirect prompt injection because it processes untrusted source code and PR diffs using shell-based analysis tools without defining boundary markers or sanitization procedures.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

Signed by skilld at 346603c. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub last month.

Steadyupdated 5 months ago
metadata
{
  "author": "agent-skills",
  "version": "1.0.0"
}

README badge

README badge for asyrafhussin/agent-skills/code-slop