Anti-Pattern 1: God Object

A God Object is a class that has too many responsibilities, handling diverse logic that should be spread across multiple classes. This anti-pattern leads to code that’s difficult to maintain, extend, or debug, as changes often affect multiple areas of functionality.

Example of a God Object


class OrderProcessor {
    processOrder(order) {
        // Validating order
        // Processing payment
        // Updating inventory
        // Sending notification
    }
}

In this example, `OrderProcessor` handles too many tasks, making it a God Object. Instead, responsibilities should be distributed across separate classes.

Solution: Distribute Responsibilities


class OrderValidator {
    validate(order) { /* validation logic */ }
}

class PaymentProcessor {
    process(payment) { /* payment processing logic */ }
}

class InventoryUpdater {
    update(item) { /* inventory update logic */ }
}

class NotificationService {
    sendNotification(order) { /* notification logic */ }
}

// Usage
const validator = new OrderValidator();
const paymentProcessor = new PaymentProcessor();
const inventoryUpdater = new InventoryUpdater();
const notifier = new NotificationService();

if (validator.validate(order)) {
    paymentProcessor.process(order.payment);
    inventoryUpdater.update(order.item);
    notifier.sendNotification(order);
}

By distributing responsibilities, each class handles a single responsibility, making the code easier to maintain and extend.

Anti-Pattern 2: Magic Numbers

Magic Numbers are hard-coded values that lack context, making code difficult to understand and prone to errors. They often appear without explanation, leading to confusion about their purpose.

Example of Magic Numbers


const discountPrice = originalPrice * 0.9; // What does 0.9 represent?

In this example, `0.9` is a Magic Number. Instead, it’s better to define it as a constant with a descriptive name.

Solution: Use Named Constants


const DISCOUNT_RATE = 0.9;
const discountPrice = originalPrice * DISCOUNT_RATE;

Using named constants like `DISCOUNT_RATE` makes the code clearer, indicating the purpose of the value and making it easier to update if needed.

Anti-Pattern 3: Tight Coupling

Tight coupling occurs when classes depend heavily on each other, making it difficult to modify or test them independently. Tight coupling limits flexibility and increases the chance that changes in one class will impact another.

Example of Tight Coupling


class Order {
    constructor() {
        this.paymentProcessor = new PaymentProcessor();
    }

    processPayment() {
        this.paymentProcessor.process();
    }
}

In this example, `Order` is tightly coupled to `PaymentProcessor`, making it difficult to test or replace `PaymentProcessor` without modifying `Order`.

Solution: Use Dependency Injection


class Order {
    constructor(paymentProcessor) {
        this.paymentProcessor = paymentProcessor;
    }

    processPayment() {
        this.paymentProcessor.process();
    }
}

// Usage
const paymentProcessor = new PaymentProcessor();
const order = new Order(paymentProcessor);

By using Dependency Injection, `Order` can work with any implementation of `PaymentProcessor`, improving flexibility and testability.

Anti-Pattern 4: Copy-Paste Programming

Copy-Paste Programming occurs when similar code is duplicated in multiple places, leading to inconsistent behavior and making code harder to maintain. Changes in one location must be replicated in every instance of the duplicated code.

Example of Copy-Paste Programming


// Repeated code
function calculateDiscount(price) { return price * 0.9; }
function calculateSalePrice(price) { return price * 0.9; }

Instead of duplicating similar logic, it’s better to create a reusable function.

Solution: Create a Reusable Function


function applyDiscount(price, rate = 0.9) {
    return price * rate;
}

// Usage
const discountPrice = applyDiscount(price);
const salePrice = applyDiscount(price);

Creating a reusable function reduces redundancy, making the code more concise and maintainable.

Anti-Pattern 5: Spaghetti Code

Spaghetti Code is unstructured and overly complex code that lacks clear organization, making it difficult to read and understand. This often results from mixing logic without modularity, leading to a tangled web of code.

Example of Spaghetti Code


function processOrder(order) {
    if (order.valid) {
        console.log("Processing payment");
        if (order.stock) {
            console.log("Updating stock");
        } else {
            console.log("Out of stock");
        }
    } else {
        console.log("Invalid order");
    }
}

This code lacks structure, making it hard to follow. Breaking it into smaller functions improves clarity.

Solution: Modularize Code


function validateOrder(order) {
    return order.valid;
}

function processPayment(order) {
    console.log("Processing payment");
}

function updateStock(order) {
    console.log(order.stock ? "Updating stock" : "Out of stock");
}

function processOrder(order) {
    if (validateOrder(order)) {
        processPayment(order);
        updateStock(order);
    } else {
        console.log("Invalid order");
    }
}

Modularizing code makes each function more readable, making it easier to understand and maintain.

Best Practices to Avoid Anti-Patterns

  • Use Single Responsibility Principle: Assign each class or function a single purpose to prevent God Objects and improve modularity.
  • Use Constants: Replace hard-coded values with named constants to improve readability and flexibility.
  • Refactor Regularly: Identify and refactor anti-patterns to maintain code quality as the application evolves.

Conclusion

Recognizing and avoiding anti-patterns like God Objects, Magic Numbers, Tight Coupling, and Spaghetti Code helps developers write clean, maintainable code. By following best practices and replacing anti-patterns with effective design techniques, developers can improve code quality, making applications easier to read, extend, and debug. Proactively addressing anti-patterns contributes to long-term software maintainability and stability.