# 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]]