multiAI Summary Pending

solid-principles

SOLID object-oriented design principles for maintainable code

231 stars

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/solid-principles/SKILL.md --create-dirs "https://raw.githubusercontent.com/aiskillstore/marketplace/main/skills/benny9193/solid-principles/SKILL.md"

Manual Installation

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

How solid-principles Compares

Feature / Agentsolid-principlesStandard Approach
Platform SupportmultiLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

SOLID object-oriented design principles for maintainable code

Which AI agents support this skill?

This skill is compatible with multi.

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

# SOLID Principles

Five principles for object-oriented design that lead to maintainable, extensible software.

## S - Single Responsibility Principle

> A class should have only one reason to change.

```typescript
// BAD - multiple responsibilities
class UserService {
  createUser(data: UserData) { /* ... */ }
  sendEmail(user: User, message: string) { /* ... */ }
  generateReport(users: User[]) { /* ... */ }
  validateEmail(email: string) { /* ... */ }
}

// GOOD - single responsibility each
class UserService {
  constructor(
    private repository: UserRepository,
    private validator: UserValidator
  ) {}

  createUser(data: UserData): User {
    this.validator.validate(data);
    return this.repository.save(data);
  }
}

class EmailService {
  send(to: string, message: string) { /* ... */ }
}

class UserReportGenerator {
  generate(users: User[]): Report { /* ... */ }
}

class EmailValidator {
  validate(email: string): boolean { /* ... */ }
}
```

**When to apply:** If you describe a class with "and" (UserService creates users AND sends emails AND...), split it.

## O - Open/Closed Principle

> Open for extension, closed for modification.

```typescript
// BAD - must modify class to add new types
class PaymentProcessor {
  process(payment: Payment) {
    if (payment.type === 'credit') {
      // process credit card
    } else if (payment.type === 'paypal') {
      // process PayPal
    } else if (payment.type === 'crypto') {
      // process crypto - had to modify!
    }
  }
}

// GOOD - extend without modification
interface PaymentMethod {
  process(amount: number): Promise<Receipt>;
}

class CreditCardPayment implements PaymentMethod {
  async process(amount: number): Promise<Receipt> {
    // credit card logic
  }
}

class PayPalPayment implements PaymentMethod {
  async process(amount: number): Promise<Receipt> {
    // PayPal logic
  }
}

// Adding crypto doesn't modify existing code
class CryptoPayment implements PaymentMethod {
  async process(amount: number): Promise<Receipt> {
    // crypto logic
  }
}

class PaymentProcessor {
  process(method: PaymentMethod, amount: number) {
    return method.process(amount);
  }
}
```

**When to apply:** When adding new features requires modifying existing, tested code.

## L - Liskov Substitution Principle

> Subtypes must be substitutable for their base types.

```typescript
// BAD - Square violates Rectangle contract
class Rectangle {
  constructor(protected width: number, protected height: number) {}

  setWidth(width: number) { this.width = width; }
  setHeight(height: number) { this.height = height; }
  getArea() { return this.width * this.height; }
}

class Square extends Rectangle {
  setWidth(width: number) {
    this.width = width;
    this.height = width; // Violates expectation!
  }
  setHeight(height: number) {
    this.width = height;
    this.height = height; // Violates expectation!
  }
}

// This breaks:
function doubleWidth(rect: Rectangle) {
  const originalHeight = rect.getArea() / rect.width;
  rect.setWidth(rect.width * 2);
  // For Square, height also doubled - unexpected!
}

// GOOD - separate hierarchies
interface Shape {
  getArea(): number;
}

class Rectangle implements Shape {
  constructor(private width: number, private height: number) {}
  getArea() { return this.width * this.height; }
}

class Square implements Shape {
  constructor(private side: number) {}
  getArea() { return this.side * this.side; }
}
```

**When to apply:** If subclass overrides change behavior that callers depend on.

## I - Interface Segregation Principle

> Clients should not depend on interfaces they don't use.

```typescript
// BAD - fat interface
interface Worker {
  work(): void;
  eat(): void;
  sleep(): void;
  attendMeeting(): void;
  writeReport(): void;
}

class Robot implements Worker {
  work() { /* ... */ }
  eat() { throw new Error('Robots do not eat'); }  // Forced to implement!
  sleep() { throw new Error('Robots do not sleep'); }
  attendMeeting() { throw new Error('Not applicable'); }
  writeReport() { throw new Error('Not applicable'); }
}

// GOOD - segregated interfaces
interface Workable {
  work(): void;
}

interface Feedable {
  eat(): void;
}

interface Sleepable {
  sleep(): void;
}

interface MeetingAttendee {
  attendMeeting(): void;
}

class Human implements Workable, Feedable, Sleepable, MeetingAttendee {
  work() { /* ... */ }
  eat() { /* ... */ }
  sleep() { /* ... */ }
  attendMeeting() { /* ... */ }
}

class Robot implements Workable {
  work() { /* ... */ }
}
```

**When to apply:** When classes implement methods they don't need, or throw "not implemented" errors.

## D - Dependency Inversion Principle

> Depend on abstractions, not concretions.

```typescript
// BAD - high-level depends on low-level
class MySQLDatabase {
  query(sql: string) { /* ... */ }
}

class UserRepository {
  private db = new MySQLDatabase();  // Tight coupling!

  findById(id: string) {
    return this.db.query(`SELECT * FROM users WHERE id = '${id}'`);
  }
}

// GOOD - both depend on abstraction
interface Database {
  query<T>(sql: string): Promise<T>;
}

class MySQLDatabase implements Database {
  async query<T>(sql: string): Promise<T> { /* ... */ }
}

class PostgreSQLDatabase implements Database {
  async query<T>(sql: string): Promise<T> { /* ... */ }
}

class UserRepository {
  constructor(private db: Database) {}  // Injected!

  findById(id: string) {
    return this.db.query(`SELECT * FROM users WHERE id = '${id}'`);
  }
}

// Easy to swap implementations
const repo = new UserRepository(new PostgreSQLDatabase());
```

**When to apply:** When testing is hard, or changing one module breaks others.

## SOLID in Practice

### Recognizing Violations

| Principle | Code Smell |
|-----------|------------|
| SRP | Class has many unrelated methods |
| OCP | Adding feature requires modifying existing code |
| LSP | Subclass throws "not supported" or behaves differently |
| ISP | Class implements methods it doesn't use |
| DIP | `new` keyword scattered throughout business logic |

### Applying SOLID

1. **Start simple** - Don't over-engineer from day one
2. **Refactor when needed** - Apply when you feel the pain
3. **Use dependency injection** - Makes DIP natural
4. **Prefer composition** - Over inheritance (helps LSP)
5. **Write small interfaces** - Easier than splitting later

### Balance

SOLID is a guide, not law. Over-applying creates:
- Too many tiny classes
- Indirection that's hard to follow
- Abstractions nobody needs yet

Apply SOLID when:
- Code is hard to test
- Changes ripple through the codebase
- Similar changes needed in multiple places
- You're adding the 3rd variation of something

## Checklist

- [ ] Does each class have a single, clear purpose?
- [ ] Can I add features without modifying existing code?
- [ ] Can subclasses replace parent classes safely?
- [ ] Are interfaces focused and minimal?
- [ ] Are dependencies injected, not created internally?