Open/Closed Principle
Open/Closed is the principle Strategy, Factory Method, Decorator, and plugin systems implement: add new behavior by extending, not by editing stable code.
Introduction
Open/Closed is the principle Strategy, Factory Method, Decorator, and plugin systems implement: add new behavior by extending, not by editing stable code. The cost: a stable abstraction must exist before variants — premature OCP is the most common over-engineering mistake.
The story
A payments team shipped fourteen providers in eighteen months without editing PaymentService. Each provider was a new class implementing PaymentGateway. The Strategy design from year one earned its cost many times over — zero regressions in core checkout.
The business problem
Without OCP, every variant edit risks every existing variant:
- Regression fear — teams delay adding providers because core file is fragile.
- Review bottleneck — core module owner reviews every variant PR.
- Coupled releases — unrelated features ship together because they touch the same file.
- Test explosion — one test file grows a case per variant.
The problem teams faced
Pre-OCP code exhibits:
- Growing switch/if chains on type or provider.
- Every new feature edits the same "core" module.
- No extension point — only modification.
Understanding the topic
OCP structure: clients depend on a stable abstraction; variants plug in without client edits.
- Stable abstraction — interface owned by the client/domain.
- Variants — new classes, not edits to existing ones.
- Composition root — wires which variant runs.
Internal architecture
Client closed, system open:
PaymentService (closed) ──> PaymentGateway (abstraction)├── StripeGateway├── RazorpayGateway└── NewProvider (add — zero PaymentService edits)
Visual explanation
OCP in three diagrams:
Informative example
Payment OCP — fourteen providers, zero core edits:
interface PaymentGateway {charge(amount: Money, customer: Customer): Promise<Receipt>;}class PaymentService {constructor(private gateway: PaymentGateway) {}async checkout(cart: Cart, customer: Customer) {const amount = cart.total();return this.gateway.charge(amount, customer);}}// New provider = new file onlyclass RazorpayGateway implements PaymentGateway {charge(amount, customer) { return razorpayClient.charge(/* ... */); }}
Execution workflow
Name variation axis
What varies? Provider, format, algorithm?
Real-world use
Browser extensions, VS Code plugins, CMS themes, webpack loaders — all OCP at product scale. Without OCP, every plugin would require editing the core product.
Production case study
Fourteen payment providers in eighteen months (above):
- Core churn: zero edits to PaymentService in eighteen months.
- Reviews: domain owners review their gateway; core team unblocked.
- Lesson: OCP pays after variant three — not variant one.
Trade-offs
- New variants ship without regression risk in core.
- Premature OCP = one-interface tax forever.
- Wrong abstraction harder to fix than no abstraction.
Decision framework
- OCP when 3+ variants confirmed or roadmap-committed.
- OCP when external teams/plugins extend behavior.
- Skip when one variant with no credible second.
Best practices
- Registry map for providers: `Record
`. - Contract tests per variant behind same interface.
- Document kill-switch if variant count stays at one for twelve months.
Anti-patterns to avoid
- Interface on day one with hypothetical variants.
- Editing core switch for each new case while calling it "Strategy."
Common mistakes
- Big-bang OCP introduction on legacy — incremental extract interface first.
Debugging tips
- Wrong variant wired? Diff composition root / DI config between envs.
Optimization strategies
- Plugin registration pattern for platforms with external authors.
Common misconceptions
- OCP ≠ never modify code. Core evolves — you avoid editing stable client when adding variants.
Advanced interview questions
Interview Prep
Practice concise answers, then expand each card for the explanation.
1AdvancedQuestionWhen does OCP cost more than it saves?+
Answer
Follow-up
2AdvancedQuestionOCP in legacy code?+
Answer
Follow-up
Summary
You can introduce OCP incrementally, map it to Strategy/Factory, and know when to wait. Tell the fourteen-provider payment story.