Command Palette

Search for a command to run...

Level 3 · 25 min

Anti-patterns

Anti-patterns are common but counterproductive solutions to recurring problems. Recognizing them in code reviews and production systems is a senior engineering skill.

God Object and Feature Envy

The God Object (God Class) knows too much or does too much — a class with hundreds of methods and fields that violates SRP catastrophically. Signs: class is imported by most other classes, changes to it break many tests, it's impossible to test in isolation. Feature Envy: a method is more interested in the data of another class than its own — calls 5 methods on an external object to compute a result that should be in that object.

Anemic Domain Model and Tight Coupling

Anemic Domain Model: domain objects are data containers with no business logic — all logic lives in service classes. This looks like OOP but is actually procedural. The Order class is just a POJO with getters/setters; OrderService has 50 methods operating on Order. Tight Coupling: modules depend directly on each other's concrete implementations. Changes in one module cascade through many. Solution: depend on interfaces, use dependency injection. The Anemic Domain Model anti-pattern was named and described by Martin Fowler (2003), building directly on the object-oriented philosophy GoF assumes throughout: "Program to an interface, not an implementation" (Gamma et al., Design Patterns, p.18). GoF's behavioral patterns — Strategy, State, Observer, Command — all require objects with real behavior. A Strategy that is merely a POJO with a flag field and all algorithm logic in a StrategyService is an anemic model masquerading as a pattern. Feature Envy (Fowler, Refactoring, p.80) is the code smell that reveals anemia: a method in OrderService that calls order.getItems(), order.getTotal(), order.getDiscount(), order.getCustomer() across ten lines is envious of Order's data — the method belongs in Order, not in the service.

Premature Optimization and Shotgun Surgery

Premature Optimization: optimizing before measuring. 'The root of all evil' (Knuth). Profile first, then optimize bottlenecks. Often the optimization makes code harder to read and maintain for zero measurable benefit. Shotgun Surgery: a single change requires modifications in many unrelated places — sign of poor cohesion. If adding a new field requires changing 15 files, the design is wrong.

Key Takeaways

  • God Objects are the most common cause of unmaintainable codebases — split them by responsibility.
  • Anemic Domain Models look like OOP but are actually procedural — move behavior to where the data lives.
  • Profile before optimizing — most perceived bottlenecks are not the actual bottlenecks.

Code example

// Anemic Domain Model (anti-pattern)
class Order { // just data
  String status; BigDecimal total; List<OrderItem> items;
  // getters and setters only
}
class OrderService { // all logic
  void confirm(Order order) {
    if (order.getItems().isEmpty()) throw new RuntimeException();
    order.setStatus("CONFIRMED");
  }
  BigDecimal calculateDiscount(Order order) { /* 30 lines */ }
}

// Rich Domain Model (correct)
class Order {
  private OrderStatus status;
  private List<OrderItem> items;

  void confirm() {
    if (items.isEmpty()) throw new DomainException("Order needs items");
    this.status = OrderStatus.CONFIRMED;
  }
  Money calculateDiscount() { /* logic belongs here */ }
}