Design Patterns Tutorial 0/70 lessons ~6 min read Lesson 11

    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.

    Course progress0%
    Focus
    21 guided sections
    Practice signal
    Examples included
    Career prep
    Interview Q&A included

    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:

    text
    PaymentService (closed) ──> PaymentGateway (abstraction)
    ├── StripeGateway
    ├── RazorpayGateway
    └── NewProvider (add — zero PaymentService edits)

    Visual explanation

    OCP in three diagrams:

    Open/Closed structure
    Client
    Stable code
    Abstraction
    Interface
    Variant A
    Existing
    Variant B
    Add — no client edit
    New variant = new file + composition root wiring — not editing Client.
    When OCP earns its cost
    Variant 1
    Inline OK
    Variant 2
    Note smell
    Variant 3+
    OCP justified
    Strategy / Factory
    Pattern choice
    Rule of Three applies — OCP without evidence is premature abstraction.
    OCP pattern map
    Algorithm varies
    Strategy
    Creation varies
    Factory
    Behavior wraps
    Decorator
    Plugins
    OCP at product scale
    Same principle — different pattern by force.

    Informative example

    Payment OCP — fourteen providers, zero core edits:

    ts
    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 only
    class RazorpayGateway implements PaymentGateway {
    charge(amount, customer) { return razorpayClient.charge(/* ... */); }
    }

    Execution workflow

    1Introduce OCP incrementally
    1 / 5

    Name variation axis

    What varies? Provider, format, algorithm?

    Git log evidence.

    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.

    2 questions
    1AdvancedQuestionWhen does OCP cost more than it saves?+

    Answer

    Wrong abstraction shape, single variant, team can't navigate indirection.

    Follow-up

    Signs abstraction is wrong?
    2AdvancedQuestionOCP in legacy code?+

    Answer

    Incremental — extract interface, move first impl, add second as new class.

    Follow-up

    Real legacy OCP story?

    Summary

    You can introduce OCP incrementally, map it to Strategy/Factory, and know when to wait. Tell the fourteen-provider payment story.

    Ready to mark this lesson complete?Track your journey across the entire course.