Design Patterns That Help Tame Legacy Code (With Python Examples)
Working with legacy code can feel like walking through a minefield. You know something might break, but you’re not sure when—or why. The good news is that design patterns can help.
In this post, I’ll share a few key design patterns that make working with legacy systems safer, cleaner, and more maintainable—using Python code examples.
🧩 1. The Adapter Pattern
Problem: Legacy code doesn’t match the interface your modern code expects.
Solution: Wrap the old code in a new interface.
Example:
Let’s say you have a legacy payment gateway:
# legacy_system.py
class LegacyPaymentProcessor:
def make_payment(self, amount):
print(f"[Legacy] Processing payment of ${amount}")
Now, you want to use a modern interface:
# adapter.py
class PaymentInterface:
def pay(self, amount):
raise NotImplementedError
class LegacyAdapter(PaymentInterface):
def __init__(self, legacy_processor):
self.legacy_processor = legacy_processor
def pay(self, amount):
return self.legacy_processor.make_payment(amount)
Usage:
from legacy_system import LegacyPaymentProcessor
from adapter import LegacyAdapter
adapter = LegacyAdapter(LegacyPaymentProcessor())
adapter.pay(100)
✅ You can now use LegacyPaymentProcessor anywhere PaymentInterface is expected.
🏗️ 2. The Facade Pattern
Problem: Legacy subsystems are complex and hard to use.
Solution: Provide a simplified interface to a larger body of code.
Example:
# legacy_subsystem.py
class LegacyAuth:
def check_user(self, username, password):
return username == "admin" and password == "1234"
class LegacyLogger:
def log(self, msg):
print(f"[LOG]: {msg}")
Facade:
class AuthFacade:
def __init__(self):
self.auth = LegacyAuth()
self.logger = LegacyLogger()
def login(self, username, password):
if self.auth.check_user(username, password):
self.logger.log(f"{username} logged in.")
return True
self.logger.log("Invalid login attempt.")
return False
✅ Consumers don’t need to deal with the legacy details.
🧪 3. The Decorator Pattern
Problem: You want to add features without changing legacy code.
Solution: Wrap the legacy object to extend behavior dynamically.
Example:
class LegacyReporter:
def report(self):
print("Generating basic report...")
class TimestampedReporter:
def __init__(self, wrapped):
self.wrapped = wrapped
def report(self):
from datetime import datetime
print(f"Report generated at: {datetime.now()}")
self.wrapped.report()
Usage:
reporter = TimestampedReporter(LegacyReporter())
reporter.report()
✅ Legacy functionality extended without touching the original class.
🧰 4. The Strategy Pattern
Problem: You want to change algorithms used by legacy code without modifying it.
Solution: Inject behavior at runtime.
Example:
class LegacySorter:
def sort(self, data):
return sorted(data) # default
# New strategy
class ReverseSortStrategy:
def sort(self, data):
return sorted(data, reverse=True)
# Updated to accept strategy
class SorterContext:
def __init__(self, strategy):
self.strategy = strategy
def sort(self, data):
return self.strategy.sort(data)
Usage:
sorter = SorterContext(ReverseSortStrategy())
print(sorter.sort([5, 1, 4, 2]))
✅ Clean separation of algorithms for flexible extension.
🚦 5. The Proxy Pattern
Problem: You need to add access control, caching, or logging to legacy classes.
Solution: Create a stand-in object that controls access to the real one.
Example:
class LegacyDatabase:
def query(self, sql):
print(f"Executing SQL: {sql}")
return f"Results for: {sql}"
class LoggingProxy:
def __init__(self, db):
self.db = db
def query(self, sql):
print(f"[LOG] About to query: {sql}")
return self.db.query(sql)
✅ Seamless control over legacy components.
✨ Wrapping Up
Design patterns are not just academic tools—they’re powerful allies when refactoring or integrating legacy code. With Python’s dynamic capabilities, implementing these patterns becomes even more fluid and expressive.
When working with legacy systems, remember:
- Wrap, don’t rewrite (yet).
- Isolate risky code behind interfaces.
- Test as you refactor.
- Make your intentions clear with patterns.
Get in Touch with us
Related Posts
- 智慧农业项目为何止步于试点阶段
- Why Smart Farming Projects Fail Before They Leave the Pilot Stage
- ERP项目为何总是超支、延期,最终令人失望
- ERP Projects: Why They Cost More, Take Longer, and Disappoint More Than Expected
- AI Security in Production: What Enterprise Teams Must Know in 2026
- 弹性无人机蜂群设计:具备安全通信的无领导者容错网状网络
- Designing Resilient Drone Swarms: Leaderless-Tolerant Mesh Networks with Secure Communications
- NumPy广播规则详解:为什么`(3,)`和`(3,1)`行为不同——以及它何时会悄悄给出错误答案
- NumPy Broadcasting Rules: Why `(3,)` and `(3,1)` Behave Differently — and When It Silently Gives Wrong Answers
- 关键基础设施遭受攻击:从乌克兰电网战争看工业IT/OT安全
- Critical Infrastructure Under Fire: What IT/OT Security Teams Can Learn from Ukraine’s Energy Grid
- LM Studio代码开发的系统提示词工程:`temperature`、`context_length`与`stop`词详解
- LM Studio System Prompt Engineering for Code: `temperature`, `context_length`, and `stop` Tokens Explained
- LlamaIndex + pgvector: Production RAG for Thai and Japanese Business Documents
- simpliShop:专为泰国市场打造的按需定制多语言电商平台
- simpliShop: The Thai E-Commerce Platform for Made-to-Order and Multi-Language Stores
- ERP项目为何失败(以及如何让你的项目成功)
- Why ERP Projects Fail (And How to Make Yours Succeed)
- Payment API幂等性设计:用Stripe、支付宝、微信支付和2C2P防止重复扣款
- Idempotency in Payment APIs: Prevent Double Charges with Stripe, Omise, and 2C2P













