DEV Community

Sachin Kasana
Sachin Kasana

Posted on • Originally published at javascript.plainenglish.io on

SOLID Principles in JavaScript & TypeScript — Real-World Examples You Can Actually Use

🚀 SOLID Principles in JavaScript & TypeScript — Real-World Examples You Can Actually Use

“A good architecture allows major decisions to be deferred.” — Uncle Bob

SOLID design Principle

Let’s face it — you’ve probably heard of SOLID principles in every other tech interview, but when it comes to JavaScript/TypeScript projects , they’re rarely applied or even understood correctly.

In this post, we’ll break down each SOLID principle with real-world examples  — think e-commerce apps , notification systems , payment integrations , and more. You’ll see when to use each principle, why it matters, and how to write clean, testable, maintainable code in JS/TS.

🧱 Quick Refresher: What Is SOLID?

  • S  — Single Responsibility Principle
  • O  — Open/Closed Principle
  • L  — Liskov Substitution Principle
  • I  — Interface Segregation Principle
  • D  — Dependency Inversion Principle

Now let’s dive into each one using examples that matter 👇

1️⃣ Single Responsibility Principle (SRP)

“A class should have only one reason to change.”

💥 Bad Example (God Class)

class OrderService {
  createOrder() { /* logic */ }
  calculateDiscount() { /* logic */ }
  sendInvoiceEmail() { /* logic */ }
}
Enter fullscreen mode Exit fullscreen mode

This class does too much — it’s handling order creation, pricing, and communication.

✅ Better SRP Example

class OrderCreator {
  create(orderData) { /* persist to DB */ }
}

class DiscountCalculator {
  apply(order) { /* promo code logic */ }
}

class EmailService {
  sendInvoice(order) { /* SMTP/SendGrid etc. */ }
}
Enter fullscreen mode Exit fullscreen mode

Now each class does one thing, and one thing well. Easier to test, refactor, and reuse.

2️⃣ Open/Closed Principle (OCP)

“Software should be open for extension, but closed for modification.”

⚠️ Problem:

You need to support different types of shipping methods — flat rate, express, pickup.

❌ Bad Design (if-else hell)

function calculateShipping(type, order) {
  if (type === 'flat') return 5;
  if (type === 'express') return 10;
  if (type === 'pickup') return 0;
}
Enter fullscreen mode Exit fullscreen mode

Every new shipping method means editing this function. Not scalable.

✅ Better OCP Design

interface ShippingStrategy {
  calculate(order): number;
}

class FlatRateShipping implements ShippingStrategy {
  calculate(order) { return 5; }
}

class ExpressShipping implements ShippingStrategy {
  calculate(order) { return 10; }
}

class PickupShipping implements ShippingStrategy {
  calculate(order) { return 0; }
}

class ShippingContext {
  constructor(private strategy: ShippingStrategy) {}

  getShippingCost(order) {
    return this.strategy.calculate(order);
  }
}
Enter fullscreen mode Exit fullscreen mode

To add a new method (e.g., drone delivery), just create a new class — no core logic touched ✅

3️⃣ Liskov Substitution Principle (LSP)

“If S is a subtype of T, then T can be replaced with S without breaking the program.”

⚠️ Use Case: Notification system

You have multiple notifiers — email, SMS, push.

❌ Anti-pattern

class Notifier {
  send(message: string) {}
}

class EmailNotifier extends Notifier {
  send(message) { /* works */ }
}

class SMSNotifier extends Notifier {
  send(message) { throw "Not implemented" }
}
Enter fullscreen mode Exit fullscreen mode

You can’t substitute Notifier with SMSNotifier safely — violates LSP.

✅ LSP-Friendly Design

interface Notifier {
  send(message: string): void;
}

class EmailNotifier implements Notifier {
  send(msg) { console.log(`Email: ${msg}`); }
}

class SMSNotifier implements Notifier {
  send(msg) { console.log(`SMS: ${msg}`); }
}
Enter fullscreen mode Exit fullscreen mode

Any class implementing Notifier can now be safely substituted.

4️⃣ Interface Segregation Principle (ISP)

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

🤯 Real Scenario:

A User interface handles registration, login, analytics, and notifications.

❌ Overloaded Interface

interface IUser {
  register(): void;
  login(): void;
  trackUsage(): void;
  sendNotification(): void;
}
Enter fullscreen mode Exit fullscreen mode

Not every User implementation needs all four.

✅ Segregated Interfaces

interface AuthUser {
  register(): void;
  login(): void;
}

interface Trackable {
  trackUsage(): void;
}

interface Notifiable {
  sendNotification(): void;
}

class BasicUser implements AuthUser {
  register() {}
  login() {}
}
Enter fullscreen mode Exit fullscreen mode

Now each implementation depends only on what it needs.

5️⃣ Dependency Inversion Principle (DIP)

“Depend on abstractions, not on concrete implementations.”

⚠️ Scenario: A controller uses a direct DB class.

class MongoDB {
  save(data) { /* ... */ }
}

class ProductController {
  private db = new MongoDB(); // tightly coupled
}
Enter fullscreen mode Exit fullscreen mode

What if you switch to PostgreSQL or Firebase? Everything breaks.

✅ DIP-Based Design

interface Database {
  save(data): void;
}

class MongoDB implements Database {
  save(data) { /* ... */ }
}

class ProductController {
  constructor(private db: Database) {}

  create(data) {
    this.db.save(data);
  }
}
Enter fullscreen mode Exit fullscreen mode

Now ProductController is agnostic to the database. You can swap MongoDB with anything.

✅ TL;DR

Real world use cases and examples

🎯 Final Thoughts

If you’ve ever struggled with spaghetti code, adding features without breaking things, or testing tightly coupled logic — SOLID is your friend.

It’s not just for “big enterprise apps.” These patterns scale beautifully in Node.js , TypeScript , React , or even simple Express backends.

Write code that your future self (or teammate) will thank you for.

https://gist.github.com/sachinkasana/4c8e7758b13b757196dc1e67363de79d

👋 Enjoyed this post?

I write about practical software design, performance, clean code, and real-world developer patterns. You can find more of my work here:

🌐 https://sachinkasana-dev.vercel.app

Thank you for being a part of the community

Before you go:


Top comments (0)