HeoLab
ToolsBlogAboutContact
HeoLab

Free developer tools with AI enhancement. Built for developers who ship.

Tools

  • JSON Formatter
  • JWT Decoder
  • Base64 Encoder
  • Timestamp Converter
  • Regex Tester
  • All Tools →

Resources

  • Blog
  • What is JSON?
  • JWT Deep Dive
  • Base64 Explained

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 HeoLab. All rights reserved.

Tools work in your browser. Zero data retention.

HomeBlogSOLID Principles Explained with JavaScript Examples
Table of Contents▾
  • Table of Contents
  • S — Single Responsibility Principle
  • O — Open/Closed Principle
  • L — Liskov Substitution Principle
  • I — Interface Segregation Principle
  • D — Dependency Inversion Principle
deep-dives#solid#design-principles#javascript

SOLID Principles Explained with JavaScript Examples

A practical walkthrough of all five SOLID principles with before/after JavaScript code examples that you can apply immediately.

Trong Ngo
February 26, 2026
3 min read

Table of Contents

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

S — Single Responsibility Principle

A class should have only one reason to change.

// Bad — UserService does too much
class UserService {
  createUser(data) { /* ... */ }
  sendWelcomeEmail(user) { /* ... */ }   // should be EmailService
  logUserCreation(user) { /* ... */ }    // should be Logger
}

// Good
class UserService {
  constructor(emailService, logger) {
    this.emailService = emailService;
    this.logger = logger;
  }
  async createUser(data) {
    const user = await db.users.create(data);
    await this.emailService.sendWelcome(user);
    this.logger.info('user_created', { userId: user.id });
    return user;
  }
}

O — Open/Closed Principle

Open for extension, closed for modification.

// Bad — add new discount type = modify existing function
function calculateDiscount(order, type) {
  if (type === 'percentage') return order.total * 0.1;
  if (type === 'fixed') return 10;
  // Must edit this every time
}

// Good — add new type without changing existing code
const discountStrategies = {
  percentage: (order) => order.total * 0.1,
  fixed: () => 10,
  vip: (order) => order.total * 0.2,
};

function calculateDiscount(order, type) {
  return discountStrategies[type]?.(order) ?? 0;
}

L — Liskov Substitution Principle

Subtypes must be substitutable for their base types.

// Bad — Square breaks Rectangle's contract
class Rectangle {
  setWidth(w) { this.width = w; }
  setHeight(h) { this.height = h; }
  area() { return this.width * this.height; }
}
class Square extends Rectangle {
  setWidth(w) { this.width = w; this.height = w; } // violates LSP
}

// Good — separate classes, no broken inheritance
class Shape {
  area() { throw new Error('Not implemented'); }
}
class Rectangle extends Shape {
  constructor(w, h) { super(); this.w = w; this.h = h; }
  area() { return this.w * this.h; }
}
class Square extends Shape {
  constructor(s) { super(); this.s = s; }
  area() { return this.s * this.s; }
}

I — Interface Segregation Principle

No client should be forced to depend on methods it does not use.

// Bad — all plugins must implement everything
class Plugin {
  onInstall() {}
  onUninstall() {}
  onUpdate() {}
  onRender() {}  // not all plugins render!
}

// Good — small focused interfaces (mixins)
const Installable = (Base) => class extends Base {
  onInstall() {}
  onUninstall() {}
};
const Renderable = (Base) => class extends Base {
  onRender() {}
};
class MyPlugin extends Installable(Renderable(class {})) {}

D — Dependency Inversion Principle

Depend on abstractions, not concretions.

// Bad — UserService tightly coupled to MySQL
import { MySQLDatabase } from './mysql';
class UserService {
  constructor() { this.db = new MySQLDatabase(); }
  getUser(id) { return this.db.query(`SELECT * FROM users WHERE id = ${id}`); }
}

// Good — depends on an abstraction (interface contract)
class UserService {
  constructor(userRepository) {  // inject any repo: MySQL, Postgres, in-memory
    this.repo = userRepository;
  }
  getUser(id) { return this.repo.findById(id); }
}

SOLID principles are most valuable when applied to code that changes often. Don't over-engineer — apply them where complexity and change frequency justify it.

Related Articles

Web Performance Optimization: A Practical Developer's Guide

3 min read

Unit Testing Best Practices in JavaScript

3 min read

TypeScript Advanced Types You Should Know

3 min read

Back to Blog

Table of Contents

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

Related Articles

Web Performance Optimization: A Practical Developer's Guide

3 min read

Unit Testing Best Practices in JavaScript

3 min read

TypeScript Advanced Types You Should Know

3 min read