DEV Community

Cover image for Understanding SOLID Principles (Without Falling Asleep) 💯
Ali Samir
Ali Samir

Posted on

Understanding SOLID Principles (Without Falling Asleep) 💯

Ever read about SOLID and felt your eyelids droop halfway through the “S”? Let’s fix that.

Whether you're debugging spaghetti code at 2 AM or trying to prevent future you from rage-quitting, SOLID principles are here to help. But let's be honest—they’re often taught in the driest way possible.

In this article, I’ll walk you through the SOLID principles using TypeScript, real-world analogies, and some much-needed humor. No jargon. No hand-waving. Just clean, readable code examples you’ll understand and remember.


Why SOLID Matters (And Why It’s Often Boring)

SOLID is a set of five design principles that make your code:

  • Easier to maintain

  • Easier to test

  • Easier to extend without breaking things

Yet, most explanations feel like they were written by a robot for other robots. That ends now.

Let’s break each principle down like we're refactoring your first bootcamp project—one bug at a time.


🧱 S – Single Responsibility Principle (SRP)

"One class should have one, and only one, reason to change."

Translation: One class. One job. No multitasking.

🚨 The Violation:

class User {
  constructor(public username: string, public password: string) {}

  login() {
    // auth logic
  }

  saveToDB() {
    // db logic
  }

  logActivity() {
    // logging logic
  }
}
Enter fullscreen mode Exit fullscreen mode

This class is doing way too much. It’s authenticating, persisting data, and logging. Next it’ll be making coffee.

✅ The Fix:

class AuthService {
  login(username: string, password: string) {
    // auth logic
  }
}

class UserRepository {
  save(user: User) {
    // db logic
  }
}

class Logger {
  log(message: string) {
    console.log(message);
  }
}

class User {
  constructor(public username: string, public password: string) {}
}
Enter fullscreen mode Exit fullscreen mode

Each class now has one job. You just earned a future-you high five.


🚪 O – Open/Closed Principle (OCP)

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

Translation: Add new features by adding code, not rewriting old code.

🚨 The Violation:

class Payment {
  pay(method: string) {
    if (method === 'paypal') {
      // PayPal logic
    } else if (method === 'stripe') {
      // Stripe logic
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Every time you add a new payment method, you have to edit this class. That’s a maintenance landmine.

✅ The Fix:

interface PaymentMethod {
  pay(amount: number): void;
}

class PayPal implements PaymentMethod {
  pay(amount: number) {
    console.log(`Paid ${amount} using PayPal`);
  }
}

class Stripe implements PaymentMethod {
  pay(amount: number) {
    console.log(`Paid ${amount} using Stripe`);
  }
}

class PaymentProcessor {
  constructor(private method: PaymentMethod) {}

  process(amount: number) {
    this.method.pay(amount);
  }
}
Enter fullscreen mode Exit fullscreen mode

Now we can add a new payment method without touching existing logic. That’s OCP in action.


🦢 L – Liskov Substitution Principle (LSP)

"Subtypes must be substitutable for their base types."

Translation: If Dog extends Animal, it should still behave like an Animal.

🚨 The Violation:

class Rectangle {
  constructor(public width: number, public height: number) {}

  setWidth(w: number) {
    this.width = w;
  }

  setHeight(h: number) {
    this.height = h;
  }

  area() {
    return this.width * this.height;
  }
}

class Square extends Rectangle {
  setWidth(w: number) {
    this.width = w;
    this.height = w;
  }

  setHeight(h: number) {
    this.width = h;
    this.height = h;
  }
}
Enter fullscreen mode Exit fullscreen mode

Try using Square where Rectangle is expected and you'll get surprising behavior. That breaks LSP.

✅ The Fix:

Don’t extend Rectangle for Square. Either compose or create separate interfaces. LSP prefers behavioral compatibility over inheritance convenience.


🧩 I – Interface Segregation Principle (ISP)

"Clients shouldn’t be forced to depend on methods they don’t use."

Translation: Keep your interfaces lean and focused.

🚨 The Violation:

interface Machine {
  print(): void;
  scan(): void;
  fax(): void;
}

class OldPrinter implements Machine {
  print() {
    console.log("Printing...");
  }

  scan() {
    throw new Error("Not supported");
  }

  fax() {
    throw new Error("Not supported");
  }
}
Enter fullscreen mode Exit fullscreen mode

Why should a printer implement scan and fax?

✅ The Fix:

interface Printer {
  print(): void;
}

interface Scanner {
  scan(): void;
}

class SimplePrinter implements Printer {
  print() {
    console.log("Printing...");
  }
}
Enter fullscreen mode Exit fullscreen mode

Now classes only implement what they actually need. The principle is happy. So is your error log.


🔌 D – Dependency Inversion Principle (DIP)

"Depend on abstractions, not concretions."

Translation: Don’t hardcode your dependencies. Use interfaces.

🚨 The Violation:

class UserService {
  private db = new Database(); // tight coupling

  getUser(id: string) {
    return this.db.find(id);
  }
}
Enter fullscreen mode Exit fullscreen mode

You can’t swap Database with MockDatabase or CloudDatabase. Testing? Good luck.

✅ The Fix:

interface IDatabase {
  find(id: string): any;
}

class UserService {
  constructor(private db: IDatabase) {}

  getUser(id: string) {
    return this.db.find(id);
  }
}
Enter fullscreen mode Exit fullscreen mode

Now you can inject any database implementation. Your code is more flexible—and testable.


🔑 Key Takeaways

  • SRP: One class = one job. Keep it focused.

  • OCP: Extend behavior without changing existing code.

  • LSP: Subclasses should work like their parents.

  • ISP: Don’t force classes to implement what they don’t need.

  • DIP: Code to interfaces, not implementations.


Let’s Keep This Going 🚀

Which SOLID principle trips you up the most? Drop a comment—let’s discuss!

If this helped, share it with a friend who still thinks SRP is a WiFi protocol.


🌐 Connect With Me On:

📍 LinkedIn
📍 X (Twitter)
📍 Telegram
📍 Instagram

Happy Coding!

Top comments (2)

Collapse
 
mahdijazini profile image
Mahdi Jazini

Thank you for the engaging article and clear explanations! It was really great and definitely useful for us developers. Appreciate you taking the time to make these complex concepts easy to understand.

Collapse
 
alisamir profile image
Ali Samir

Thank you!