DEV Community

Cover image for πŸ›οΈ 5 Pillars of SOLID
Sajidur Rahman Shajib
Sajidur Rahman Shajib

Posted on

πŸ›οΈ 5 Pillars of SOLID

When it comes to writing clean and maintainable code, the SOLID principles are essential tools every developer should know. But here’s the truth: it's not just about memorizing five fancy terms β€” it's about recognizing when and how to apply them in real code. You don’t need to recite the definitions β€” you need to practice them.
The more you apply these principles, the more they become second nature, improving your design decisions over time. Keep this guide handy, and focus on using SOLID whenever you see the opportunity.

Letter Principle Name Abbreviation Key Idea
S Single Responsibility Principle SRP One class = One responsibility
O Open/Closed Principle OCP Open to extension, closed to modification
L Liskov Substitution Principle LSP Subtypes must be substitutable for base types
I Interface Segregation Principle ISP No forcing of unused methods; favor small, focused interfaces
D Dependency Inversion Principle DIP Depend on abstractions, not concrete implementations

🧱 S β€” Single Responsibility Principle (SRP)

βœ… A class should have only one reason to change. Each class should handle a single part of the functionality.

πŸ”₯ Bad Example:

class UserManager:
    def add_user(self, user):
        # Add user to database
        pass

    def send_email(self, user, message):
        # Send welcome email
        pass
Enter fullscreen mode Exit fullscreen mode

βœ… Good Example:

class UserManager:
    def add_user(self, user):
        # Add user to database
        pass

class EmailService:
    def send_email(self, user, message):
        # Send email
        pass
Enter fullscreen mode Exit fullscreen mode

🎯 When & Why:

Use SRP to make your code easier to understand, test, and modify. Changes in one responsibility won't break others.

🧱 O β€” Open/Closed Principle (OCP)

βœ… Software should be open for extension but closed for modification.

πŸ”₯ Bad Example:

class Discount:
    def get_discount(self, customer_type):
        if customer_type == "regular":
            return 0.1
        elif customer_type == "vip":
            return 0.2
Enter fullscreen mode Exit fullscreen mode

βœ… Good Example:

class Discount:
    def get_discount(self):
        return 0.0

class RegularDiscount(Discount):
    def get_discount(self):
        return 0.1

class VIPDiscount(Discount):
    def get_discount(self):
        return 0.2
Enter fullscreen mode Exit fullscreen mode

🎯 When & Why:

Use it when you expect requirements to evolve. This allows you to add new behavior with new classes without touching existing tested code.

🧱 L β€” Liskov Substitution Principle (LSP)

βœ… Subclasses should be replaceable for their parent classes without altering program behavior.

πŸ”₯ Bad Example:

class Bird:
    def fly(self):
        print("Flying")

class Penguin(Bird):
    def fly(self):
        raise Exception("Penguins can't fly")
Enter fullscreen mode Exit fullscreen mode

βœ… Good Example:

class Bird:
    pass

class FlyingBird(Bird):
    def fly(self):
        print("Flying")

class Penguin(Bird):
    def swim(self):
        print("Swimming")
Enter fullscreen mode Exit fullscreen mode

🎯 When & Why:

The Liskov Substitution Principle (LSP) applies when using inheritance. It ensures that a subclass can stand in for its parent class without breaking the program.

🧱 I β€” Interface Segregation Principle (ISP)

βœ… Clients should not be forced to depend on methods they do not use.

πŸ”₯ Bad Example:

class Machine:
    def print(self): pass
    def scan(self): pass
    def fax(self): pass

class OldPrinter(Machine):
    def print(self): pass
    def scan(self): raise NotImplementedError()
    def fax(self): raise NotImplementedError()
Enter fullscreen mode Exit fullscreen mode

βœ… Good Example:

class Printer:
    def print(self): pass

class Scanner:
    def scan(self): pass

class Fax:
    def fax(self): pass

class OldPrinter(Printer):
    def print(self): pass
Enter fullscreen mode Exit fullscreen mode

🎯 When & Why:

Use ISP to keep interfaces lean and focused. This avoids forcing classes to implement irrelevant functionality.

D β€” Dependency Inversion Principle (DIP)

βœ… High-level modules should depend on abstractions, not on low-level modules.

πŸ”₯ Bad Example:

class MySQLDatabase:
    def connect(self):
        pass

class DataService:
    def __init__(self):
        self.db = MySQLDatabase()
Enter fullscreen mode Exit fullscreen mode

βœ… Good Example:

class Database:
    def connect(self):
        pass

class MySQLDatabase(Database):
    def connect(self):
        pass

class DataService:
    def __init__(self, db: Database):
        self.db = db
Enter fullscreen mode Exit fullscreen mode

🎯 When & Why:

Use DIP to make your code more testable and flexible. It allows you to swap out implementations (e.g., use a mock DB during tests).

βœ… Conclusion

The SOLID principles are timeless guidelines for writing clean, modular, and maintainable object-oriented code. By following:

  • SRP β€” keep responsibilities focused,

  • OCP β€” allow easy feature expansion,

  • LSP β€” ensure consistent behavior in subclasses,

  • ISP β€” build focused and usable interfaces,

  • DIP β€” depend on abstractions for flexibility,

you create software that is easier to test, scale, and adapt to change. Applying SOLID isn't about rigid rules β€” it's about writing thoughtful, future-proof code.

Top comments (0)