Skip to main content
Time to Master: ~30 minutes | Difficulty: Beginner-friendly | Prerequisite: Basic OOP understanding

🏗️ What are SOLID Principles?

Imagine building with LEGO blocks:
  • Good LEGO sets: Pieces fit together nicely, easy to change, fun to build
  • Bad LEGO sets: Pieces stuck together with glue, can’t change anything without breaking
SOLID principles are five rules that help you write code like good LEGO sets - modular, flexible, and maintainable!

S

Single Responsibility

O

Open/Closed

L

Liskov Substitution

I

Interface Segregation

D

Dependency Inversion

🎯 Why Learn SOLID?

🔧 Easy to Change

Modify one thing without breaking everything else

🐛 Fewer Bugs

Simpler classes = fewer places for bugs to hide

🧪 Easy to Test

Small, focused classes are easy to test

👥 Team Friendly

Others can understand and work with your code
Interview Alert: SOLID principles are asked in almost every LLD interview! “Does your design follow SOLID?” is a common follow-up question.

📚 The Five Principles at a Glance

S - Single Responsibility Principle (SRP)

“Do ONE thing, and do it well!”
Like how a chef COOKS, a waiter SERVES, and a cashier handles PAYMENT - each has one job.
# ❌ BAD: UserManager does EVERYTHING
class UserManager:
    def create_user(self): pass
    def send_email(self): pass      # Email is a different job!
    def generate_report(self): pass  # Reporting is different too!

# ✅ GOOD: Each class has ONE job
class UserService:
    def create_user(self): pass

class EmailService:
    def send_email(self): pass

class ReportService:
    def generate_report(self): pass

O - Open/Closed Principle (OCP)

“Open for extension, closed for modification!”
Like adding new apps to your phone without rewriting the operating system.
# ❌ BAD: Must modify when adding new shapes
class AreaCalculator:
    def calculate(self, shape):
        if shape.type == "circle":
            return 3.14 * shape.radius ** 2
        elif shape.type == "rectangle":  # Must keep adding elif!
            return shape.width * shape.height

# ✅ GOOD: Add new shapes without changing existing code
class Shape:
    def area(self): pass

class Circle(Shape):
    def area(self):
        return 3.14 * self.radius ** 2

class Rectangle(Shape):
    def area(self):
        return self.width * self.height

# Adding Triangle? Just create new class, don't touch existing!

L - Liskov Substitution Principle (LSP)

“Children should be able to replace their parents!”
If you expect an Animal, a Dog should work perfectly - without surprises.
# ❌ BAD: Penguin breaks the Bird contract
class Bird:
    def fly(self): pass

class Penguin(Bird):  # But penguins can't fly! 🐧
    def fly(self):
        raise Exception("I can't fly!")  # Surprise!

# ✅ GOOD: Separate flying and non-flying birds
class Bird:
    def eat(self): pass

class FlyingBird(Bird):
    def fly(self): pass

class Penguin(Bird):  # No fly() method to break
    def swim(self): pass

I - Interface Segregation Principle (ISP)

“Don’t force me to implement things I don’t need!”
Like a printer that only prints - it shouldn’t need a fax feature if you don’t use fax.
# ❌ BAD: Forces all workers to implement every method
class Worker:
    def work(self): pass
    def eat(self): pass
    def sleep(self): pass

class Robot(Worker):  # Robots don't eat or sleep!
    def eat(self): raise Exception("I don't eat!")
    def sleep(self): raise Exception("I don't sleep!")

# ✅ GOOD: Separate interfaces
class Workable:
    def work(self): pass

class Eatable:
    def eat(self): pass

class Robot(Workable):  # Only implements what it needs
    def work(self): pass

D - Dependency Inversion Principle (DIP)

“Depend on abstractions, not concrete things!”
Like a phone charger - you plug into the WALL SOCKET (abstraction), not directly into power lines!
# ❌ BAD: High-level depends on low-level details
class PaymentProcessor:
    def __init__(self):
        self.stripe = StripeAPI()  # Directly tied to Stripe!
    
    def pay(self, amount):
        self.stripe.charge(amount)  # What if we switch to PayPal?

# ✅ GOOD: Depend on abstraction
class PaymentGateway:  # Abstract
    def charge(self, amount): pass

class PaymentProcessor:
    def __init__(self, gateway: PaymentGateway):  # Accept ANY gateway
        self.gateway = gateway
    
    def pay(self, amount):
        self.gateway.charge(amount)  # Works with Stripe, PayPal, anything!

🎮 Interactive Memory Aid

Remember SOLID with this story:
1

S is for Single Chef 👨‍🍳

The chef only COOKS - doesn’t clean, serve, or manage money
2

O is for Open Restaurant 🏪

Add new dishes to the menu WITHOUT rewriting the kitchen
3

L is for Like Parent, Like Child 👨‍👧

If menu says “pasta”, any pasta type should work the same
4

I is for I Only Take Orders 📝

Waiter takes orders - doesn’t need to know cooking
5

D is for Depend on Rules 📋

Kitchen depends on RECIPE standards, not specific ingredients

🧪 Quick Self-Test

Q1: Which principle does this violate?
class Report:
    def generate_data(self): pass
    def format_as_pdf(self): pass
    def send_email(self): pass

Q2: Which principle does this violate?
class Calculator:
    def calculate(self, operation, a, b):
        if operation == "add": return a + b
        elif operation == "subtract": return a - b
        # Must modify here for each new operation!

Q3: Which principle does this violate?
class Machine:
    def print_doc(self): pass
    def scan_doc(self): pass
    def fax_doc(self): pass
    def copy_doc(self): pass

class SimplePrinter(Machine):  # Only prints!
    def scan_doc(self): raise NotImplementedError
    def fax_doc(self): raise NotImplementedError
    def copy_doc(self): raise NotImplementedError

📚 The SOLID Journey

Now let’s dive deep into each principle with fun examples and exercises!

🏃 Start Your Journey!

Begin with Single Responsibility Principle →

Learn the first and most important SOLID principle - keeping your classes focused!