SOLID Principles

The five foundational principles of object-oriented design that lead to maintainable, scalable software.

4 min read2026-03-22easysolidoopdesign-principles

What is SOLID?

SOLID is a mnemonic for five design principles that help create maintainable, flexible, and understandable object-oriented code.

PrincipleFull Name
SSingle Responsibility Principle
OOpen/Closed Principle
LLiskov Substitution Principle
IInterface Segregation Principle
DDependency Inversion Principle

S — Single Responsibility Principle

Definition

A class should have one and only one reason to change.

// ❌ Bad: Multiple responsibilities
class UserService {
  createUser(data: UserData) { /* ... */ }
  sendEmail(user: User, template: string) { /* ... */ }
  generateReport(users: User[]) { /* ... */ }
}

// ✅ Good: Single responsibility each
class UserService {
  createUser(data: UserData) { /* ... */ }
}

class EmailService {
  sendEmail(to: string, template: string) { /* ... */ }
}

class ReportService {
  generateReport(users: User[]) { /* ... */ }
}

O — Open/Closed Principle

Definition

Software entities should be open for extension but closed for modification.

// ❌ Bad: Must modify class for each new shape
class AreaCalculator {
  calculate(shape: any): number {
    if (shape.type === 'circle') return Math.PI * shape.radius ** 2;
    if (shape.type === 'rectangle') return shape.width * shape.height;
    // Must add new if-else for each shape...
  }
}

// ✅ Good: Extend without modifying
interface Shape {
  area(): number;
}

class Circle implements Shape {
  constructor(private radius: number) {}
  area(): number { return Math.PI * this.radius ** 2; }
}

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

L — Liskov Substitution Principle

Definition

Subtypes must be substitutable for their base types without altering the correctness of the program.

I — Interface Segregation Principle

Definition

Clients should not be forced to depend on interfaces they do not use.

// ❌ Bad: Fat interface
interface Worker {
  work(): void;
  eat(): void;
  sleep(): void;
}

// A robot can't eat or sleep!
class Robot implements Worker {
  work() { /* ... */ }
  eat() { throw new Error("Robots don't eat"); }  // Forced!
  sleep() { throw new Error("Robots don't sleep"); }
}

// ✅ Good: Segregated interfaces
interface Workable { work(): void; }
interface Eatable { eat(): void; }
interface Sleepable { sleep(): void; }

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

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

D — Dependency Inversion Principle

Definition

High-level modules should not depend on low-level modules. Both should depend on abstractions.

// ❌ Bad: High-level depends on low-level
class OrderService {
  private mysqlDb = new MySQLDatabase();
  
  saveOrder(order: Order) {
    this.mysqlDb.insert('orders', order); // Tightly coupled!
  }
}

// ✅ Good: Both depend on abstraction
interface Database {
  insert(table: string, data: any): void;
}

class OrderService {
  constructor(private db: Database) {} // Injected!
  
  saveOrder(order: Order) {
    this.db.insert('orders', order);
  }
}

Interview Application

SOLID principles are foundational for LLD interviews. When designing a system, explicitly mention which principles guide your class structure.

Comments