# Open Closed Principle (OCP) The **Open Closed Principle (OCP)** is the "O" in [[SOLID Principles]]. It states that **software entities should be open for extension but closed for modification** — you should be able to change a system's behavior by *adding* new code, not by *editing* existing code that is already working and trusted. First formulated by [[Bertrand Meyer]] in *Object-Oriented Software Construction* (1988) using inheritance as the extension mechanism. [[Robert C. Martin]] later refined the principle to use **abstractions** (interfaces, abstract classes) rather than direct subclassing, which composes better with the rest of [[SOLID Principles]] (especially [[Liskov Substitution Principle (LSP)]] and [[Dependency Inversion Principle (DIP)]]). ## Why it matters Code that works has earned its trust through testing and time in production. Editing it risks breaking that trust — every change is a potential regression. OCP says: when new behavior is needed, prefer mechanisms that *add* a new module rather than *modify* an existing one. The trusted code keeps working untouched; the new behavior is isolated and can be tested independently. This is what makes long-lived systems maintainable. A codebase that requires editing 10 files to add a new variant has no OCP discipline; one where you add a single new class with a known interface and the system picks it up has it. ## How extension points are built The pattern: identify the axis of variation, define an abstraction across it, and wire concrete implementations through that abstraction. Common mechanisms (in order of decreasing intrusion): | Mechanism | Extension model | |-----------|----------------| | Interface + multiple implementations | New behavior = new class implementing the interface ([[Strategy Pattern]]) | | Abstract class with hooks | New behavior = subclass that fills in primitives ([[Template Method]]) | | Event/observer registration | New behavior = register a new listener ([[Observer Pattern]]) | | Plugin discovery / registry | New behavior = drop in a module the host loads at startup | | Configuration-driven dispatch | New behavior = add a new config entry pointing to a handler | In every case, the *core* code knows only the abstraction; the *variants* depend on the abstraction; new variants ship as additions, not edits. This is exactly the dependency direction the [[Dependency Inversion Principle (DIP)]] prescribes. ## A test you can apply > *Can I implement this new feature without editing any file that currently has tests passing for unrelated functionality?* If yes — the system is open to extension along that axis. If no — every new variant forces edits to a central file (a long `switch`, a registry mutated in place, a base class with a growing API). That's OCP violation, and the central file is a coupling hotspot that will keep growing. ## OCP is axis-specific, not absolute A class is open for extension *along some specific dimension* and closed against modification *for that dimension*. No code is open along every possible axis — you can't anticipate every future change without making the design unworkable. The discipline: when the same change happens repeatedly, recognize the axis, then refactor to make that axis open. Don't try to predict axes that haven't manifested. [[Robert C. Martin]] phrases this as a heuristic: take your first hit; on the second similar change, recognize the pattern and *then* introduce the abstraction. Premature OCP is over-engineering. ## Common smells of OCP violation - A `switch` or chain of `if/else` keyed on a "type" enum that grows every time a new type is added. - A central `Registry`, `Factory`, or `Manager` whose source must be edited for every new entity. - Bug fixes for one variant break unrelated variants because they share a long method. - Adding a new business rule requires touching multiple files spread across the codebase. ## OCP done well, in practice - HTTP middleware stacks (add a new middleware without editing the existing ones). - Plugin systems for editors, browsers, build tools. - [[Strategy Pattern]]-based selection of algorithms. - Domain event handlers in event-driven architectures. The system's *core* loop never changes; new capability is grafted on at well-defined seams. ## Tradeoffs - **Indirection**: every extension point adds a layer between caller and concrete behavior. Too many layers and the code becomes hard to follow. - **Premature abstraction**: extension points designed for hypothetical futures become dead weight. - **Discoverability**: a system fully governed by registration and dispatch can hide *what actually runs* under runtime configuration. The right amount of OCP is "enough to absorb the changes that actually happen, not the ones that might." ## References - Bertrand Meyer — *Object-Oriented Software Construction* (1988, 1997) - Robert C. Martin — *The Open-Closed Principle* (1996, C++ Report) - Robert C. Martin — *Clean Architecture* (2017), Chapter 8 ## Related - [[SOLID Principles]] - [[Single Responsibility Principle (SRP)]] - [[Liskov Substitution Principle (LSP)]] - [[Dependency Inversion Principle (DIP)]] - [[Strategy Pattern]] - [[Template Method]] - [[Observer Pattern]] - [[Composition over Inheritance]] - [[Inversion of Control (IoC)]] - [[Robert C. Martin]]