Skip to content

🧱 SOLID Principles ​

Five principles for writing maintainable object-oriented code.

S β€” Single Responsibility ​

A class should have only one reason to change.

typescript
// BAD: UserService handles auth, profile, and notifications
class UserService {
  login() { ... }
  updateProfile() { ... }
  sendWelcomeEmail() { ... }
}

// GOOD: Each class has one job
class AuthService { login() { ... } }
class ProfileService { updateProfile() { ... } }
class NotificationService { sendWelcomeEmail() { ... } }

O β€” Open/Closed ​

Open for extension, closed for modification.

Add new behavior by adding new code, not changing existing code. Use interfaces and composition instead of modifying existing classes.

L β€” Liskov Substitution ​

Subtypes must be substitutable for their base types.

If Bird has a fly() method, Penguin extends Bird breaks the contract. Solution: restructure the hierarchy or use composition.

I β€” Interface Segregation ​

Don't force clients to depend on methods they don't use.

typescript
// BAD: One fat interface
interface Worker {
  code(): void
  design(): void
  manage(): void
}

// GOOD: Focused interfaces
interface Coder { code(): void }
interface Designer { design(): void }
interface Manager { manage(): void }

D β€” Dependency Inversion ​

Depend on abstractions, not concrete implementations.

typescript
// BAD: Directly depends on PostgreSQL
class UserService {
  private db = new PostgresDatabase()
}

// GOOD: Depends on an interface
class UserService {
  constructor(private db: Database) {}
}

Practical application ​

  • Don't apply SOLID dogmatically β€” it's a guide, not law
  • Most useful in large codebases with multiple contributors
  • For small projects, KISS often trumps SOLID
  • Start simple, refactor toward SOLID when complexity grows
  • The goal is maintainability, not pattern purity

Pergame Knowledge Base