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.

rulesover-eng-single-method-class.md

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

Single-Method Class That Should Be a Function

Impact: HIGH (Wraps a free function in class ceremony for no reason)

A class with exactly one public method, no state, and no dependencies should usually be a function. AI generates these because its training data is enterprise Java/C# where classes are mandatory for everything. PHP and TypeScript both support free functions / static methods / single-purpose action classes — the wrapping ceremony is pure slop.

The exception: invokable action classes (Laravel convention) using __invoke() are legitimate when they have constructor-injected dependencies and represent a named domain action. The slop variant is the class with no constructor, no state, one static method, and no clear domain identity.

Incorrect

// ❌ Class wrapping a single pure function

class StringFormatter
{
    public static function slugify(string $input): string
    {
        return Str::slug($input);
    }
}

class EmailValidator
{
    public function isValid(string $email): bool
    {
        return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
    }
}

class TimestampHelper
{
    public static function toIso(DateTime $dt): string
    {
        return $dt->format(DateTimeInterface::ATOM);
    }
}
// ❌ Same in TS
class StringUtil {
  static capitalize(s: string): string {
    return s.charAt(0).toUpperCase() + s.slice(1);
  }
}

class ResponseFormatter {
  static format(data: unknown): ApiResponse {
    return { data, timestamp: Date.now() };
  }
}

Why it's slop:

  • Callers write StringFormatter::slugify(...) instead of slugify(...)
  • new EmailValidator()->isValid($email) is six tokens for what should be one
  • The class adds no encapsulation (no state to encapsulate)
  • Importing/autoloading the class wastes bytes for zero benefit
  • Pattern is recognisably "Java port" — PHP and TS have first-class functions

Correct

// ✅ Free function or method on the existing domain type

// helpers.php (or composer autoload "files")
function slugify(string $input): string
{
    return Str::slug($input);
}

function isValidEmail(string $email): bool
{
    return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
}

// Or extension methods on Carbon
class CustomCarbon extends Carbon
{
    public function toIso(): string { return $this->format(DateTimeInterface::ATOM); }
}
// ✅ Free exported functions
export function capitalize(s: string): string {
  return s.charAt(0).toUpperCase() + s.slice(1);
}

export function formatResponse<T>(data: T): ApiResponse<T> {
  return { data, timestamp: Date.now() };
}

Why it reads human:

  • Caller writes slugify(input) — one token, no ceremony
  • No autoloader hit, no class instantiation, no dependency to mock in tests
  • Functions are testable directly with no setup

Single-method class is OK when…

These are legitimate, NOT slop:

// ✅ Invokable action with injected dependencies — Laravel idiom
final class PlaceOrder
{
    public function __construct(
        private PaymentGateway $payments,
        private InventoryService $inventory,
    ) {}

    public function __invoke(OrderRequest $request): Order { /* ... */ }
}

// ✅ Job / command — meant to be queued
final class SendWeeklyDigest implements ShouldQueue
{
    public function handle(MailerService $mailer): void { /* ... */ }
}

// ✅ Form Request — Laravel pattern
final class StoreUserRequest extends FormRequest { /* ... */ }

The test: does it have state, dependencies, or a domain identity beyond "I wrap a function"? If yes, it's a class. If no, it should be a function.

Detection

# PHP — classes with exactly one public method and no constructor injection
# (rough heuristic: file has 'class X' + exactly one 'public function')
for f in $(find app/ -name '*.php'); do
  PUBLIC=$(grep -cE '^\s+public function ' "$f")
  CTOR=$(grep -cE '^\s+public function __construct' "$f")
  PROPS=$(grep -cE '^\s+(private|protected) (readonly )?[a-zA-Z]+ \$' "$f")
  if [ "$PUBLIC" = "1" ] && [ "$CTOR" = "0" ] && [ "$PROPS" = "0" ]; then
    echo "SUSPECT (single-method, no state): $f"
  fi
done

# TS — classes with one method and no constructor or fields
grep -rln 'class\s\+[A-Z]' --include='*.ts' src/ | while read f; do
  METHODS=$(grep -cE '^\s+(public\s+|private\s+|protected\s+)?[a-zA-Z]+\s*\(' "$f")
  CTOR=$(grep -c 'constructor' "$f")
  if [ "$METHODS" = "1" ] && [ "$CTOR" = "0" ]; then
    echo "SUSPECT: $f"
  fi
done

Reference: Internal: naming-suffix-abuse, over-eng-useless-wrapper

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