<arch.design/>
Principles/Open/Closed Principle
{ }CodeArchitectureintermediate1988solidmeyerextensionabstraction

Open/Closed Principle

Software entities should be open for extension but closed for modification — add new behaviour by writing new code, not by changing existing code.

4/5
{ }
Operates at: Code level

Inside a codebase — classes, modules, files

How it works

The Open/Closed Principle (OCP) was formulated by Bertrand Meyer in 1988 and popularised by Robert C. Martin as part of SOLID. A module is 'open' when you can add new behaviour to it; it is 'closed' when existing callers are not broken by that addition.

The implementation technique is abstraction: instead of a class knowing the concrete details of all its variants, it depends on an interface. New variants are new classes that implement the interface — no existing class is touched.

A classic example: a DiscountCalculator with a switch statement over discount types. Every new type requires editing the switch. With OCP, DiscountStrategy is an interface; each discount is a class. Adding a new discount is adding a new file — the calculator is untouched and its tests still pass.

OCP is particularly powerful in plugin architectures, where third parties can extend behaviour without having access to (or being able to modify) the core.

Implementation

TypeScript · Go · Rust
// ❌ Every new discount type requires editing this method
class DiscountCalculator {
  calculate(order: Order, discountType: string): number {
    if (discountType === "student") return order.total * 0.10;
    if (discountType === "senior")  return order.total * 0.15;
    if (discountType === "vip")     return order.total * 0.20;
    // ❌ adding "employee" means opening this file again
    return 0;
  }
}

// ✓ Add a new discount by adding a new class — nothing existing changes
interface DiscountStrategy {
  calculate(order: Order): number;
}

class StudentDiscount  implements DiscountStrategy {
  calculate(o: Order) { return o.total * 0.10; }
}
class SeniorDiscount   implements DiscountStrategy {
  calculate(o: Order) { return o.total * 0.15; }
}
class VIPDiscount      implements DiscountStrategy {
  calculate(o: Order) { return o.total * 0.20; }
}
// ✓ New discount = new file, zero changes to existing code
class EmployeeDiscount implements DiscountStrategy {
  calculate(o: Order) { return o.total * 0.30; }
}

class DiscountCalculator {
  calculate(order: Order, strategy: DiscountStrategy): number {
    return strategy.calculate(order);
  }
}

Why it matters

Every time you modify an existing class to add a new variant, you risk breaking existing behaviour. OCP lets you extend the system by addition — safer, and it keeps existing tests green.

When to use

  • When a class has a growing switch/if-else over types or variants
  • Plugin architectures where third parties extend behaviour
  • When you want to add new variants without running existing test suites again
  • Frameworks and libraries — callers extend by subclassing or implementing interfaces

When NOT to use

  • Premature abstraction before you have two real variants — YAGNI applies
  • Not all variation is OCP-worthy — sometimes an if statement is the right answer

Trade-offs

+

New variants are addable without touching existing, tested code

Requires upfront design — the right abstraction must be chosen before extensions arrive

+

Reduces regression risk — closed modules don't need retesting for new extensions

Too many extension points create 'shotgun surgery' when the interface itself needs changing

+

Natural fit for plugin and extension architectures

Over-engineering risk — not every class needs to be extensible

In production

VS Code

Extension API — VS Code's core is closed; extensions add language support, themes, debuggers without modifying the editor

Jest

Custom matchers and reporters extend Jest without forking it — open for extension, closed for modification

Spring

BeanPostProcessor, ApplicationListener — the container is extensible without requiring source changes

Industry adoption

4/5Widely adopted — mainstream at medium-to-large engineering orgs.

Related principles