Skip to content
LLD Learn/SOLID Principles
Browsing as a guest. Sign in to save your progress and earn XP as you complete chapters.

Open/Closed Principle

6 min read

You'll learn to

  • -Design classes that are open for extension but closed for modification, typically via polymorphism over conditionals
  • -Recognize a growing if/else or switch chain as the signal that OCP is being violated

The Open/Closed Principle states that a class should be open for extension but closed for modification - you should be able to add new behavior without editing code that already works and is already tested. The mechanism that makes this possible is almost always polymorphism: instead of branching on type inside one method, define a common interface and let each type provide its own implementation.

The Growing Switch Statement Is the Tell

The most common OCP violation in an interview whiteboard is a method that branches on a type or enum, and where every new case requires editing that same method again. A discount calculator that starts with if type == "student"... and grows an elif for every new discount type is technically working code, but each new discount type means re-touching, re-testing, and re-risking a method that already worked for every previous case.

From a growing switch to extension without modification
# Before: every new discount type means editing this method again
def calculate_discount(customer_type: str, price: float) -> float:
    if customer_type == "student":
        return price * 0.9
    elif customer_type == "senior":
        return price * 0.85
    elif customer_type == "military":  # <- added later, method edited again
        return price * 0.8
    return price

# After: closed for modification, open for extension
class DiscountStrategy(ABC):
    @abstractmethod
    def apply(self, price: float) -> float: ...

class StudentDiscount(DiscountStrategy):
    def apply(self, price: float) -> float: return price * 0.9

class MilitaryDiscount(DiscountStrategy):  # <- new file, zero existing code touched
    def apply(self, price: float) -> float: return price * 0.8

OCP Is Not "Never Change Anything"

This principle does not mean existing code is frozen forever - bug fixes and genuine requirement changes to existing behavior are normal and expected. OCP specifically targets the case of adding a new variant of existing behavior: a new discount type, a new payment method, a new notification channel. The goal is that "add a new kind of X" should mean writing a new class, not re-opening a method that every existing kind of X already depends on.

OCP and Strategy (a later chapter) are close cousins: OCP is the principle, Strategy is very often the concrete pattern used to satisfy it when the varying behavior is "which algorithm or rule applies here."

Interview Signal

You see a NotificationSender class with a send(type, message) method containing if type == "email" / elif type == "sms" branches. How would you flag this?

Weak Answer

"That's fine, it works and handles both cases correctly."

Strong Answer

"It works today, but every new channel means editing this method again and re-risking the existing branches - that's an Open/Closed violation. I'd define a NotificationChannel interface with an EmailChannel and SmsChannel implementation, and have the sender iterate over a list of channels instead of branching on type. A new channel becomes a new class, not a new elif."

Check Yourself1 / 3

What does "open for extension, closed for modification" mean in practice?

ScaleDojo Logo
Initializing ScaleDojo