skip to content

Protected Variations

Predict where change is likely, then wrap that point in a stable interface so the variation cannot ripple outward. It is the generalization that OCP, DIP and information hiding are all special cases of.

part ofSoftware design & architectureoverview, primer and where to startread it →
on this pageshow

questions

6

What is the GRASP principle "Protected Variations", and what problem is it meant to solve?

level: juniorimportance: must knowfreq 55%

answer

  1. predict the variation point
  2. stable interface wraps the volatile thing
  3. clients bind to contract, never past it
  4. meta-principle over OCP, DIP, info hiding
  5. Larman GRASP; Cockburn 1996

basics

~20 s

Protected Variations says: find the parts most likely to change or vary, and put a stable interface in front of them. Other code depends on that interface, so a change stays contained instead of rippling outward.

solid answer

~50 s

Protected Variations (PV) is one of the GRASP principles (General Responsibility Assignment Software Patterns, from Craig Larman). Its question is: how do you design so that instability in one element does not damage other elements? Its answer: identify points of predicted variation or instability, and assign responsibilities so that a stable interface surrounds them. Clients then depend only on that interface; the volatile thing lives behind it and can be swapped, rewritten, or multiplied without touching callers. "Stable" means the interface's shape and semantics change far less often than what it hides. PV is deliberately a meta-principle: information hiding, encapsulation, the Open/Closed Principle, the Dependency Inversion Principle, polymorphism, indirection, data-driven design and service lookup are all concrete ways of achieving it. The cost is an extra layer of abstraction, so PV is applied where variation is genuinely predicted, not everywhere.

code

pseudocode · 16 lines
pseudocode
// Unprotected: the checkout depends on a concrete, volatile provider.
checkout(order):
    stripe = StripeClient(apiKey)
    stripe.charge(order.total, order.card)   // swap provider => edit here

// Protected: a stable interface wraps the predicted variation point.
interface PaymentGateway:
    authorize(amount, instrument) -> AuthResult   // stable vocabulary,
                                                  // no provider terms leak

checkout(order, gateway: PaymentGateway):
    gateway.authorize(order.total, order.instrument)

// Variation now lives only in implementations:
//   StripeGateway, AdyenGateway, SandboxGateway
// Adding a provider adds a class; checkout() is untouched.

go deeper

for a junior

State the problem/solution pair: predicted variation, stable interface around it, clients bind to the interface. Give one concrete example such as swapping a payment provider or a storage backend.

for a middle

Add the mechanisms (polymorphism, indirection, data-driven design, service lookup) and name PV as the generalisation behind OCP, DIP and information hiding.

for a senior

Bring in cost. Distinguish variation points from evolution points, discuss speculative generality and the rule of three, and describe how you choose the seam so the abstraction doesn't leak.

for a principal

Talk at system scale — anti-corruption layers, ports and adapters, versioned contracts, plugin boundaries — and frame protection as buying an option: worth it when the predicted change is likely and the retrofit cost is high.

## The terms, defined first - **GRASP** = *General Responsibility Assignment Software Patterns*, a set of nine principles catalogued by Craig Larman for deciding **which object gets which responsibility**. Members include Information Expert, Creator, Controller, Low Coupling, High Cohesion, Polymorphism, Pure Fabrication, Indirection, and **Protected Variations**. - **Coupling** = how much one element must know about another. If A calls a concrete class B and B changes, A may have to change too — that is a *ripple*. - **Variation point** = a place where the system must support *several* behaviours **right now** (e.g. three payment providers, two tax rules). - **Evolution point** = a place where variation is *anticipated for the future* but is not required today. - **Stable interface** = a contract (method names, parameters, meanings, error behaviour) that is expected to change much more slowly than the code hiding behind it. ## The principle, stated > **Problem:** How do you design objects, subsystems and systems so that variations or instability in these elements do not have an undesirable impact on other elements? > > **Solution:** Identify the points of predicted variation or instability; assign responsibilities to create a **stable interface** around them. This formulation is Larman's; he credits Alistair Cockburn's 1996 IEEE Software article *"Protected Variation: The Importance of Being Closed"*, which in turn ties the idea back to David Parnas's **information hiding** (1972 — modularise around the decisions most likely to change) and Bertrand Meyer's **Open/Closed Principle** (open for extension, closed for modification). ## How it actually works Three steps: 1. **Predict.** Ask where change is likely: external systems and APIs, formats and protocols, business rules and pricing, regulations and tax, hardware/devices, UI technology, persistence technology, algorithms with competing implementations, anything a competitor/customer will demand a variant of. 2. **Wrap.** Introduce something stable in front: an interface/abstract type, a facade, an adapter, a configuration table, a lookup service, an event. 3. **Bind clients to the stable thing.** Callers must not be able to reach past the wrapper. If clients can still see the volatile type (via a leaked return type, a downcast, a public field, a thrown provider-specific exception), the protection is nominal only. ## Why it is called a *meta*-principle PV is the generalisation; the others are instances of it: | Mechanism | What variation it protects against | |---|---| | Encapsulation / information hiding | Variation in internal representation and data structures | | Interfaces + polymorphism | Variation in behaviour among a family of types | | Open/Closed Principle | Change by *adding* new code rather than editing existing code | | Dependency Inversion Principle | High-level policy varying because a low-level detail varied | | Indirection (adapter, facade, proxy) | Variation in an external or legacy component | | Data-driven / config-driven design | Variation in values, rules, mappings — moved out of code entirely | | Service lookup / dependency injection | Variation in *which* implementation and *where* it comes from | | Interpreter / rules engine | Variation in logic, expressed as data at runtime | | Law of Demeter | Variation in the *object graph structure* you navigate | | Standard formats/protocols | Variation between vendors | ## The cost, and why PV is not "always yes" Every protective seam adds a level of indirection: more types, harder navigation ("jump to definition" lands on an interface), harder debugging, and a real risk of **speculative generality** — Fowler's smell for an abstraction built for a variation that never arrives. Larman's own guidance is that **variation points** (needed now) almost always justify protection, while **evolution points** (speculative) need judgement: protect when the *predicted* change is likely, the *cost of being wrong later* is high, and the protection is cheap. A common heuristic is the **rule of three**: the first case is concrete, the second is duplicated deliberately, the third earns the abstraction — by then you can see the real axis of variation instead of guessing it. ## Edge cases and failure modes - **Wrong seam.** You wrapped the database but the variation was in the *query semantics*; the interface leaks anyway. - **Lowest-common-denominator interface.** Forcing several providers behind one contract can strip away the capabilities that made each provider worth having. - **Leaky abstraction.** Timeouts, retries, pagination, transaction scope and error taxonomies from the hidden component surface through the interface, so callers end up coding to the implementation regardless. - **Protection with no client.** Only one implementation ever exists and none is expected — pure overhead. ## One-line summary PV = *decide what will change, put a stable contract in front of it, and forbid anyone from reaching around the contract.*

  • Does Protected Variations mean you should put an interface in front of everything?
    No. It applies to *predicted* points of variation or instability. A universal wrapper policy produces speculative generality: extra indirection, more types, harder navigation, and abstractions shaped around guesses rather than real axes of change. Stable, self-contained, unlikely-to-change code should stay concrete.
  • What makes an interface 'stable' in this context?
    Its shape *and* its semantics change far less often than what it hides: the vocabulary is expressed in the client's domain rather than the provider's, it does not leak provider-specific types, errors or lifecycle, and adding a new implementation does not force it to grow new methods.

A wall socket. Appliance makers protect themselves from variation in power generation (coal, nuclear, solar, a diesel generator in a storm) by binding only to a stable interface: shape of the plug, voltage, frequency. Everything upstream can be replaced without rewiring a single lamp — and the moment an appliance needs to know which power plant it is on, the protection has leaked.

saying these in an interview costs you the question

  • Reducing PV to "use interfaces everywhere" — the principle is about *identifying predicted variation*, not adding indirection uniformly.
  • Claiming PV is the same thing as encapsulation. Encapsulation is one mechanism among many (polymorphism, config-driven design, service lookup, indirection, standard formats).
  • Assuming an interface automatically means protection, while the concrete type still leaks through return types, downcasts or provider-specific exceptions.
  • Saying PV is only an object-oriented, class-level idea — Larman states it applies at object, subsystem *and* system scale.
  • Treating PV as free: it always buys flexibility with indirection, and unused flexibility is a cost, not an asset.

context

open as a page

How does Protected Variations relate to the Open/Closed Principle, the Dependency Inversion Principle, and Parnas's information hiding? Is it the same idea restated?

level: middleimportance: must knowfreq 45%

basics

~20 s

They aim at the same goal from different angles. Protected Variations is the general statement — wrap predicted change behind a stable interface. Open/Closed, Dependency Inversion and information hiding are specific, more prescriptive ways of doing exactly that.

open as a page

Protected Variations tells you to wrap predicted points of instability. How do you decide *which* points genuinely deserve protection, and when does applying it make the design worse?

level: seniorimportance: must knowfreq 30%

basics

~20 s

Protect where change is genuinely likely and expensive to retrofit — external systems, business rules, regulated logic, vendor choices. Skip it where change is unlikely or cheap to make later; unused abstractions are pure cost: more indirection, harder reading, harder debugging.

open as a page

Beyond interfaces and polymorphism, what concrete mechanisms can be used to achieve Protected Variations, and what are the trade-offs of each?

level: middleimportance: should knowfreq 35%

basics

~20 s

Interfaces are only one option. You can also move variation into data or configuration, use an adapter or facade around an external system, look implementations up at runtime, publish events instead of calling recipients, or adopt a standard format so vendors are interchangeable.

open as a page

You have wrapped a third-party service behind an interface, yet a provider change still forced edits across many callers. What went wrong, and how do you diagnose and fix a protective boundary that failed?

level: seniorimportance: should knowfreq 24%

basics

~20 s

The interface probably wasn't stable: it exposed the provider's types, errors, or behaviour. If callers must know which implementation is behind the interface — for retries, error codes, ordering or quirks — the variation leaked and nothing was truly protected.

open as a page

How does Protected Variations manifest at architecture scale rather than class scale, and what makes an architectural boundary genuinely protective?

level: principalimportance: nice to knowfreq 16%

basics

~20 s

At architecture scale, the wrapped variation is whole systems: vendors, external partners, legacy platforms, data formats. Protection means anti-corruption layers, ports and adapters, versioned contracts and plugin points — boundaries that also match who owns and deploys each side.

open as a page