Adapter Pattern: Making Incompatible Interfaces Work Together
By Misbahul Munir3 min read605 words

Adapter Pattern: Making Incompatible Interfaces Work Together

Backend Development
design pattern
structural pattern

After finishing the creational patterns, we now enter the world of structural patterns — which are all about how classes and objects are composed to form larger structures. First up: the Adapter Pattern, which helps incompatible interfaces work together.


What Is the Adapter Pattern?

The Adapter Pattern allows objects with incompatible interfaces to work together by acting as a translator between them. It's like a plug converter: the socket expects a certain shape, and you use an adapter to make your device compatible.

It’s especially useful when:

  • You integrate third-party libraries or legacy code that can't be changed.
  • You want to conform different systems to a common interface in your application.
  • You want to write clean, decoupled code despite external inconsistencies.

Real-World Analogy: Travel Power Adapter

Imagine you're traveling from the US to Europe. Your laptop charger has a US plug, but European outlets are shaped differently. Instead of buying a new charger, you use a travel adapter to connect your existing charger to the European socket.

You're not changing the charger or the outlet — you're just translating between them. That’s exactly what the Adapter Pattern does in code.


Real Backend Example: Adapting a Third-Party Payment API

Suppose your backend system defines a generic

PaymentProcessor
interface, but now your team wants to integrate PayPal, whose SDK has a very different API. Instead of changing your whole app to match PayPal, you write an adapter.

Step 1: Define the Target Interface

# payment_processor.py from abc import ABC, abstractmethod class PaymentProcessor(ABC): @abstractmethod def pay(self, amount: float): pass

Step 2: Assume a Third-Party Class (Incompatible Interface)

# third_party/paypal_sdk.py class PayPalAPI: def __init__(self, user_email): self.email = user_email def send_payment(self, money): print(f"[PayPal] Sent ${money:.2f} from {self.email}")

This

PayPalAPI
uses
send_payment()
instead of
pay()
, and takes
money
instead of
amount
. Not compatible with our app.


Step 3: Create the Adapter

# adapters/paypal_adapter.py from payment_processor import PaymentProcessor from third_party.paypal_sdk import PayPalAPI class PayPalAdapter(PaymentProcessor): def __init__(self, email): self.paypal = PayPalAPI(email) def pay(self, amount: float): self.paypal.send_payment(amount)

Now the

PayPalAdapter
fits into your existing code that uses
PaymentProcessor
, even though
PayPalAPI
has a different interface.


Step 4: Use in the Application

# main.py from adapters.paypal_adapter import PayPalAdapter def checkout(payment_processor: PaymentProcessor, total: float): payment_processor.pay(total) paypal = PayPalAdapter("client@example.com") checkout(paypal, 99.99)

Output:

[PayPal] Sent $99.99 from client@example.com

Your application remains decoupled from the third-party API. Future changes to PayPal (or switching to Stripe) won’t affect your core logic — only the adapter needs updating.


Where Adapter Pattern Shines in Backend Systems

  • Integrating different database drivers with a common repository interface
  • Wrapping legacy classes that don’t follow your system’s interface
  • Connecting to external services (e.g., email, SMS, payment, auth)
  • Creating compatibility layers for microservices with differing contracts
  • Standardizing data format conversions (e.g., XML ↔ JSON)

Pitfalls and Considerations

  • Overusing adapters can introduce unnecessary layers if not needed.
  • Don’t confuse this with the Decorator Pattern — which adds behavior, while Adapter changes interface.
  • Make sure the adapter hides complexity, not just redirects calls.

Final Thoughts

The Adapter Pattern is one of the most practical tools in a backend engineer’s toolbox — especially when dealing with systems you don’t control. It helps you embrace the messiness of the real world without letting it leak into your clean architecture.

Think of it as a translator at a meeting: everyone speaks their native language, but still understands each other thanks to the adapter.