acc-check-leaky-abstractions

Detects leaky abstractions in PHP code. Identifies implementation details exposed in interfaces, concrete returns from abstract methods, framework leakage into domain, and infrastructure concerns in application layer.

16 stars

Best use case

acc-check-leaky-abstractions is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Detects leaky abstractions in PHP code. Identifies implementation details exposed in interfaces, concrete returns from abstract methods, framework leakage into domain, and infrastructure concerns in application layer.

Teams using acc-check-leaky-abstractions should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/acc-check-leaky-abstractions/SKILL.md --create-dirs "https://raw.githubusercontent.com/diegosouzapw/awesome-omni-skill/main/skills/development/acc-check-leaky-abstractions/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/acc-check-leaky-abstractions/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How acc-check-leaky-abstractions Compares

Feature / Agentacc-check-leaky-abstractionsStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Detects leaky abstractions in PHP code. Identifies implementation details exposed in interfaces, concrete returns from abstract methods, framework leakage into domain, and infrastructure concerns in application layer.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Leaky Abstractions Detector

## Overview

This skill analyzes PHP codebases for leaky abstractions — situations where implementation details "leak" through interface boundaries, violating encapsulation and creating tight coupling.

## Leaky Abstraction Types

| Type | Description | Severity |
|------|-------------|----------|
| Interface Leakage | Implementation details in interface | CRITICAL |
| Framework Leakage | Framework types in domain/application | CRITICAL |
| Return Type Leakage | Concrete types returned from abstractions | WARNING |
| Parameter Leakage | Implementation-specific parameters | WARNING |
| Exception Leakage | Infrastructure exceptions crossing boundaries | WARNING |
| Dependency Leakage | Inner dependencies exposed | INFO |

## Detection Patterns

### Phase 1: Interface Leakage

```bash
# Doctrine types in interfaces
Grep: "Collection|ArrayCollection|PersistentCollection" --glob "**/Domain/**/*Interface.php"
Grep: "Doctrine\\\\|Illuminate\\\\|Symfony\\\\" --glob "**/Domain/**/*Interface.php"

# Infrastructure types in domain interfaces
Grep: "Redis|Memcached|Elasticsearch|Guzzle|Http" --glob "**/Domain/**/*Interface.php"

# Database types in repository interfaces
Grep: "QueryBuilder|EntityManager|Connection|PDO" --glob "**/Domain/**/*RepositoryInterface.php"

# ORM annotations/attributes in interfaces
Grep: "#\\[ORM\\\\|@ORM\\\\|@Entity|@Table" --glob "**/Domain/**/*Interface.php"
```

**Example Violations:**
```php
// BAD: Leaky interface
interface UserRepositoryInterface
{
    public function findByQuery(QueryBuilder $query): Collection; // Doctrine leak!
}

// GOOD: Clean interface
interface UserRepositoryInterface
{
    /** @return User[] */
    public function findByCriteria(UserCriteria $criteria): array;
}
```

### Phase 2: Framework Leakage into Domain

```bash
# Symfony components in Domain
Grep: "use Symfony\\\\Component\\\\" --glob "**/Domain/**/*.php"
Grep: "use Symfony\\\\Contracts\\\\" --glob "**/Domain/**/*.php"

# Laravel components in Domain
Grep: "use Illuminate\\\\" --glob "**/Domain/**/*.php"

# Doctrine in Domain entities
Grep: "use Doctrine\\\\ORM\\\\Mapping" --glob "**/Domain/**/*.php"
Grep: "#\\[ORM\\\\|@ORM\\\\|@Entity|@Column|@ManyToOne" --glob "**/Domain/**/*.php"

# HTTP in Domain
Grep: "Request|Response|HttpFoundation" --glob "**/Domain/**/*.php"
```

**Domain Should NOT Contain:**
- Framework service containers
- HTTP request/response objects
- ORM annotations (use separate mapping files)
- Framework validators
- Framework events (use domain events)

### Phase 3: Return Type Leakage

```bash
# Concrete class returns from interface methods
Grep: "public function.*\):\s*[A-Z][a-z]+[A-Z]" --glob "**/*Interface.php"
# Should return interfaces, not concrete classes

# Collection returns instead of arrays
Grep: "): Collection|): ArrayCollection" --glob "**/Domain/**/*Interface.php"

# Nullable entity returns (might indicate infrastructure concern)
Grep: "): \?[A-Z][a-z]+\s*;|): null\|[A-Z]" --glob "**/*Interface.php"

# Framework response types
Grep: "): Response|): JsonResponse|): View" --glob "**/Application/**/*.php"
```

### Phase 4: Parameter Leakage

```bash
# ORM-specific parameters in domain methods
Grep: "function.*EntityManager|function.*Connection" --glob "**/Domain/**/*.php"

# Query parameters
Grep: "function.*QueryBuilder|function.*Criteria\s*\$" --glob "**/Domain/**/*.php"

# HTTP request as parameter
Grep: "function.*Request \$request" --glob "**/Application/**/*UseCase.php"
Grep: "function.*Request \$request" --glob "**/Application/**/*Handler.php"

# Framework config in domain
Grep: "function.*Config|function.*Parameters" --glob "**/Domain/**/*.php"
```

### Phase 5: Exception Leakage

```bash
# Database exceptions in domain
Grep: "throw.*Doctrine\\\\|catch.*Doctrine\\\\" --glob "**/Domain/**/*.php"
Grep: "throw.*PDOException|catch.*PDOException" --glob "**/Domain/**/*.php"

# HTTP exceptions in application
Grep: "throw.*HttpException|throw.*NotFoundHttpException" --glob "**/Application/**/*.php"

# Infrastructure exceptions crossing boundaries
Grep: "catch.*\\\\Infrastructure\\\\" --glob "**/Application/**/*.php"

# Missing exception translation
Grep: "catch.*Exception" --glob "**/Infrastructure/**/*Repository.php" -A 3
# Should translate to domain exceptions
```

### Phase 6: Dependency Leakage

```bash
# Constructor exposing internal dependencies
Grep: "__construct.*EntityManager|__construct.*Connection" --glob "**/Application/**/*.php"

# Public methods with infrastructure types
Grep: "public function.*Logger|public function.*Cache" --glob "**/Domain/**/*.php"

# Getter exposing internal state
Grep: "public function get.*\(\).*EntityManager|public function get.*\(\).*Repository" --glob "**/*.php"
```

### Phase 7: Serialization Leakage

```bash
# JSON serialization in domain
Grep: "JsonSerializable|jsonSerialize" --glob "**/Domain/**/*.php"

# Symfony serializer attributes in domain
Grep: "#\\[Serializer\\\\|#\\[Groups|@Groups" --glob "**/Domain/**/*.php"

# API platform attributes in domain
Grep: "#\\[ApiResource|#\\[ApiProperty" --glob "**/Domain/**/*.php"
```

## Report Format

```markdown
# Leaky Abstractions Report

## Summary

| Leak Type | Critical | Warning | Info |
|-----------|----------|---------|------|
| Interface Leakage | 2 | 3 | - |
| Framework Leakage | 4 | 2 | - |
| Return Type Leakage | - | 5 | 3 |
| Parameter Leakage | 1 | 4 | - |
| Exception Leakage | 2 | 3 | - |
| Dependency Leakage | - | 2 | 4 |

**Total Leaks:** 8 critical, 19 warnings, 7 info

## Critical Issues

### LEAK-001: Doctrine Collection in Interface
- **File:** `src/Domain/User/UserRepositoryInterface.php:12`
- **Issue:** ORM-specific type in domain interface
- **Code:**
  ```php
  public function findActive(): Collection;
  ```
- **Expected:**
  ```php
  /** @return User[] */
  public function findActive(): array;
  ```
- **Impact:** Domain tied to Doctrine, cannot switch ORM
- **Skills:** `acc-create-repository`

### LEAK-002: Framework in Domain Entity
- **File:** `src/Domain/Order/Entity/Order.php:8`
- **Issue:** Doctrine ORM annotations in domain entity
- **Code:**
  ```php
  use Doctrine\ORM\Mapping as ORM;

  #[ORM\Entity]
  #[ORM\Table(name: 'orders')]
  class Order
  ```
- **Expected:** Use XML/YAML mapping files in Infrastructure
- **Impact:** Domain depends on persistence framework

### LEAK-003: HTTP Request in UseCase
- **File:** `src/Application/UseCase/CreateOrderUseCase.php:23`
- **Issue:** HTTP Request in application layer
- **Code:**
  ```php
  public function __invoke(Request $request): Response
  ```
- **Expected:**
  ```php
  public function __invoke(CreateOrderCommand $command): OrderId
  ```
- **Impact:** UseCase tied to HTTP, cannot reuse in CLI
- **Skills:** `acc-create-command`, `acc-create-use-case`

## Warning Issues

### LEAK-004: PDOException Not Translated
- **File:** `src/Infrastructure/Repository/DoctrineUserRepository.php:45`
- **Issue:** Database exception not translated to domain exception
- **Code:**
  ```php
  public function save(User $user): void
  {
      $this->em->persist($user);
      $this->em->flush(); // PDOException can leak!
  }
  ```
- **Expected:**
  ```php
  public function save(User $user): void
  {
      try {
          $this->em->persist($user);
          $this->em->flush();
      } catch (UniqueConstraintViolationException $e) {
          throw new UserAlreadyExistsException($user->email());
      }
  }
  ```

### LEAK-005: Concrete Return Type
- **File:** `src/Application/Service/PaymentServiceInterface.php:15`
- **Issue:** Returns concrete class instead of interface
- **Code:**
  ```php
  public function process(Payment $payment): StripePaymentResult;
  ```
- **Expected:**
  ```php
  public function process(Payment $payment): PaymentResultInterface;
  ```

### LEAK-006: Infrastructure Logger in Domain
- **File:** `src/Domain/Order/Service/OrderValidator.php:12`
- **Issue:** Logger dependency in domain service
- **Code:**
  ```php
  public function __construct(
      private LoggerInterface $logger,
  ) {}
  ```
- **Expected:** Domain should not log, or use domain events

## Abstraction Boundaries

```
┌─────────────────────────────────────────────────────────────┐
│                    Presentation Layer                        │
│  Request, Response, Controller, View                         │
├─────────────────────────────────────────────────────────────┤
│                    Application Layer                         │
│  Commands, Queries, Handlers, DTOs                          │
│  ❌ No HTTP types, ❌ No framework services                  │
├─────────────────────────────────────────────────────────────┤
│                      Domain Layer                            │
│  Entities, Value Objects, Domain Services, Interfaces        │
│  ❌ No ORM, ❌ No framework, ❌ No infrastructure            │
├─────────────────────────────────────────────────────────────┤
│                   Infrastructure Layer                       │
│  Repositories, Adapters, External Services                   │
│  ✅ ORM, ✅ Framework, ✅ Database                           │
└─────────────────────────────────────────────────────────────┘
```

## Refactoring Strategies

### Interface Abstraction
| Leaky | Clean |
|-------|-------|
| `Collection` | `array` or custom `*Collection` |
| `QueryBuilder` | `Criteria` or `Specification` |
| `EntityManager` | `RepositoryInterface` |
| `Request` | `Command` / `Query` DTO |
| `Response` | Return value + Responder |

### Exception Translation
```php
// Infrastructure layer
try {
    $this->connection->execute($sql);
} catch (UniqueConstraintViolationException $e) {
    throw new DuplicateEmailException($email);
} catch (\PDOException $e) {
    throw new PersistenceException('Failed to save user', 0, $e);
}
```

### Framework Independence
```php
// Instead of Doctrine Collection
interface UserRepositoryInterface
{
    /** @return User[] */
    public function findActive(): array;
}

// Implementation can use Collection internally
class DoctrineUserRepository implements UserRepositoryInterface
{
    public function findActive(): array
    {
        return $this->createQueryBuilder('u')
            ->where('u.active = true')
            ->getQuery()
            ->getResult(); // Returns array
    }
}
```
```

## Quick Analysis Commands

```bash
# Detect leaky abstractions
echo "=== Framework in Domain ===" && \
grep -rn "use Doctrine\\|use Symfony\\|use Illuminate\\" --include="*.php" src/Domain/ && \
echo "=== ORM in Interfaces ===" && \
grep -rn "Collection|QueryBuilder|EntityManager" --include="*Interface.php" src/ && \
echo "=== HTTP in Application ===" && \
grep -rn "Request|Response|HttpFoundation" --include="*.php" src/Application/ && \
echo "=== Unhandled Exceptions ===" && \
grep -rn "throw.*PDO\|throw.*Doctrine" --include="*.php" src/Domain/ src/Application/
```

## Integration

Works with:
- `acc-structural-auditor` — Layer boundary analysis
- `acc-ddd-auditor` — Domain purity checks
- `acc-create-repository` — Clean repository interfaces
- `acc-create-anti-corruption-layer` — External system isolation

## References

- "The Law of Leaky Abstractions" — Joel Spolsky
- "Clean Architecture" (Robert C. Martin) — Dependency Rule
- "Domain-Driven Design" (Eric Evans) — Layered Architecture

Related Skills

acc-check-encapsulation

16
from diegosouzapw/awesome-omni-skill

Analyzes PHP code for encapsulation violations. Detects public mutable state, exposed internals, Tell Don't Ask violations, getter/setter abuse, and information hiding breaches.

acc-check-bounded-contexts

16
from diegosouzapw/awesome-omni-skill

Analyzes bounded context boundaries in DDD projects. Detects cross-context coupling, shared kernel violations, context mapping issues, and ubiquitous language inconsistencies. Generates context map diagrams and boundary recommendations.

a11y-self-check

16
from diegosouzapw/awesome-omni-skill

Proactively validates Claude Code's own generated HTML/JSX/TSX output for accessibility before presenting to users. Use this skill automatically when generating UI code to ensure WCAG 2.1 AA compliance.

asyncredux-check-internet-mixin

16
from diegosouzapw/awesome-omni-skill

Add the CheckInternet mixin to ensure network connectivity before action execution. Covers automatic error dialogs, combining with NoDialog for custom UI handling, and AbortWhenNoInternet for silent abort.

accessibility-design-checklist

16
from diegosouzapw/awesome-omni-skill

Эксперт по accessibility дизайну. Используй для WCAG, a11y чеклистов и inclusive design.

accessibility-design-checker

16
from diegosouzapw/awesome-omni-skill

Ensures designs meet accessibility requirements including WCAG compliance, color contrast, keyboard navigation, screen reader support, and focus management. Reviews designs for accessibility issues and provides recommendations.

accessibility-basic-check

16
from diegosouzapw/awesome-omni-skill

Run a basic accessibility checklist for UI changes. Use when a junior developer needs quick a11y guidance.

mailcheck-automation

16
from diegosouzapw/awesome-omni-skill

Automate Mailcheck tasks via Rube MCP (Composio). Always search tools first for current schemas.

ddd-check

16
from diegosouzapw/awesome-omni-skill

DDD設計原則チェッカー(AIDLC ドキュメントと実装コードの一貫性を検証)

COMPLIANCE_CHECK

16
from diegosouzapw/awesome-omni-skill

Apply the OpenAI SDK compliance checklist to audit files or directories and produce a Markdown report with findings and suggested fixes. Use when asked to "check compliance", "run compliance check", or "audit against OpenAI SDK rules".

check-ceph-health

16
from diegosouzapw/awesome-omni-skill

Check Ceph storage health on OpenShift OCS/ODF clusters. Use when PVCs are stuck in Pending, storage provisioning fails, Ceph is degraded, OSDs are full, or cluster storage needs diagnosis.

acc-check-immutability

16
from diegosouzapw/awesome-omni-skill

Analyzes PHP code for immutability violations. Checks Value Objects, Events, DTOs for readonly properties, no setters, final classes, and wither patterns. Ensures domain objects maintain invariants.