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.
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 */ }
}