0
0
LLDsystem_design~7 mins

Anti-patterns to avoid in LLD - System Design Guide

Choose your learning style9 modes available
Problem Statement
When developers follow common but flawed design habits, systems become hard to maintain, scale, and debug. These bad practices cause code duplication, tight coupling, and unpredictable behavior, leading to wasted time and costly fixes.
Solution
Recognizing and avoiding anti-patterns helps keep code clean, modular, and easy to change. By applying good design principles and patterns, developers create systems that are easier to understand, test, and extend over time.
Architecture
Spaghetti
Code
God Object
Hard to
Maintain

This diagram shows common anti-patterns leading to maintenance, testing, and fragility problems in software systems.

Trade-offs
✓ Pros
Avoiding anti-patterns improves code readability and maintainability.
Leads to easier debugging and faster feature additions.
Promotes modular design, enabling better testing and reuse.
✗ Cons
Requires upfront effort and discipline to recognize and prevent anti-patterns.
May slow initial development speed due to extra design considerations.
Learning curve for developers unfamiliar with good design principles.
Always use good design practices from the start, especially in projects expected to grow beyond a few thousand lines of code or multiple developers.
In very small, throwaway scripts or prototypes where long-term maintenance is not a concern.
Real World Examples
Amazon
Avoided monolithic god objects by adopting microservices, enabling independent scaling and deployment.
Netflix
Prevented tight coupling by using event-driven architecture, allowing services to evolve independently.
Uber
Reduced spaghetti code by enforcing clean service boundaries and clear API contracts.
Code Example
The before code mixes order types and payment logic, duplicating code and making changes risky. The after code uses the Strategy pattern to separate payment methods, making it easier to add new payment types without changing order processing.
LLD
### Before: Spaghetti code with duplicated logic and tight coupling
class OrderProcessor:
    def process(self, order):
        if order.type == 'online':
            print(f"Processing online order {order.id}")
            # duplicated payment logic
            if order.payment_method == 'card':
                print("Charging card")
            elif order.payment_method == 'paypal':
                print("Charging PayPal")
        elif order.type == 'store':
            print(f"Processing store order {order.id}")
            # duplicated payment logic
            if order.payment_method == 'card':
                print("Charging card")
            elif order.payment_method == 'paypal':
                print("Charging PayPal")

### After: Applying Strategy pattern to separate payment logic
class PaymentStrategy:
    def pay(self, order):
        pass

class CardPayment(PaymentStrategy):
    def pay(self, order):
        print("Charging card")

class PaypalPayment(PaymentStrategy):
    def pay(self, order):
        print("Charging PayPal")

class OrderProcessor:
    def __init__(self, payment_strategy: PaymentStrategy):
        self.payment_strategy = payment_strategy

    def process(self, order):
        print(f"Processing {order.type} order {order.id}")
        self.payment_strategy.pay(order)

# Usage
order = type('Order', (), {'id': 123, 'type': 'online', 'payment_method': 'card'})()
processor = OrderProcessor(CardPayment())
processor.process(order)
OutputSuccess
Alternatives
Modular Design
Breaks system into independent, well-defined modules instead of tangled code.
Use when: When you want easier maintenance and parallel development.
Design Patterns
Use proven solutions like Factory or Observer to solve common problems instead of ad-hoc code.
Use when: When facing recurring design challenges.
SOLID Principles
Guidelines to keep code flexible and decoupled, avoiding anti-patterns like god objects.
Use when: When building object-oriented systems requiring long-term maintainability.
Summary
Anti-patterns cause maintainability and scalability problems in software systems.
Avoiding them requires applying good design principles and patterns from the start.
Refactoring anti-patterns early improves code quality and reduces future costs.