Compare
Symptom
Human

Two existing classes do similar work but expose different method names, parameter shapes, or argument orders. Client code that wants to treat them uniformly branches on which one it has; each branch contains its own translation logic; new branches appear as new client sites do their own variant of the same translation.

Agent

`instanceof` branches at every consumer site the agent must verify match consistently. Per-consumer translation logic is duplicated; the agent's per-edit cost on a translation-rule change is proportional to consumer count.

Goal
Human

An Adapter wraps the off-shape class and exposes the canonical interface. Clients receive whichever conformant instance the composition root produces; no client code knows about the underlying difference.

Agent

One adapter file the agent verifies once; consumers are uniform calls against a single interface. Translation rules are centralized; consumer-side code is generic over the adapted type.

Before the refactoring

// Two classes do similar work through different shapes; callers branch on which they have.
class Logger {
log(level, message) { /* writes to internal log */ }
}
class ThirdPartyReporter {
report(severityName, text, context) { /* delivers to vendor */ }
}
function emitDiagnostic(emitter, level, message) {
if (emitter instanceof Logger) {
emitter.log(level, message);
} else if (emitter instanceof ThirdPartyReporter) {
emitter.report(level.toUpperCase(), message, {});
} else {
throw new Error('unknown emitter');
}
}

After the refactoring

// One adapter gives the vendor class the internal interface; callers stop branching.
class Logger {
log(level, message) { /* writes to internal log */ }
}
class ThirdPartyReporter {
report(severityName, text, context) { /* delivers to vendor */ }
}
class ReporterAsLogger {
constructor(reporter) {
this.reporter = reporter;
}
log(level, message) {
this.reporter.report(level.toUpperCase(), message, {});
}
}
function emitDiagnostic(logger, level, message) {
logger.log(level, message);
}
// At the composition root:
const adaptedReporter = new ReporterAsLogger(new ThirdPartyReporter());
emitDiagnostic(adaptedReporter, 'error', 'something failed');
Example source: Illustrative example written for this site, faithful to Kerievsky's pattern shape in Refactoring to Patterns (Addison-Wesley, 2004), chapter 6. Distinct from Extract Adapter (#5): that pattern pulls per-variant code from one host class into adapters; this pattern wraps an existing class so it conforms to a different existing interface.
Pressure
Human

Alternative-interface branches at every consumer duplicate the translation rules. Subtle translation bugs (wrong case, missing context argument, swapped order) ship as misrouted diagnostic data; tests for the translation live inside each consumer's tests rather than once where they belong.

Agent

Per-consumer translation defeats static typing — the consumer's interface contract is implicit in the branches rather than declared. Adding a third variant means another branch at every consumer site (Shotgun Surgery scales with consumer × variant count).

Tradeoff
Human

Each adapter is one new class; for two-variant systems with stable interfaces, the adapter may add more friction than the inline conversion did. Wrapping a third-party class in an adapter ties the adapter to that vendor's surface — vendor changes ripple through the adapter; consumers stay insulated.

Agent

Adapter spreads the boundary across one extra class; the agent must trace from consumer through adapter to vendor to understand a single call's full path. Vendor-API changes ripple to the adapter — usually the right place — but the agent must remember to re-verify the adapter when the vendor updates.

Relief
Human

Consumers operate against one interface; translation lives in one file; new consumers stop knowing the vendor's shape. Vendor swaps become an adapter change, not a consumer-population edit.

Agent

Consumers reach for the canonical interface; a new vendor variant is one new adapter file, and existing consumer code does not change because the adapter's signature matches what the consumers already call.

Trap
Human

Adapters that grow large translation logic (multi-step shaping, async/sync bridging, error-model translation) become a hidden host for non-trivial behaviour. When the adapter starts containing business rules, what was Adapter has drifted into Anti-Corruption Layer; rename and refactor accordingly.

Agent

An adapter that smuggles non-trivial logic (validation, error remapping, retries) hides behaviour the agent might expect to see at the consumer or at the vendor — neither location is now authoritative. When the adapter is doing more than shape-conversion, its name should reflect that (Gateway, Anti-Corruption Layer).