An anti-pattern is a common approach that looks reasonable at first but leads to problems as a codebase grows — recognizing them is as valuable as knowing the good patterns.
God Object — a single class that knows or does far too much, often accumulating responsibilities over time until it becomes a tangled, thousand-line bottleneck that every other part of the system depends on. Prevented by consistently applying the Single Responsibility Principle from the SOLID chapter.
Anemic Domain Model — classes that are pure data bags (all public properties, no meaningful methods), with all the actual logic living in separate 'service' classes. This isn't inherently wrong for every architecture, but it abandons much of the value of encapsulation covered earlier — the data and the rules governing it end up disconnected, which is exactly what OOP was designed to prevent.
Deep Inheritance Hierarchies — chains like A → B → C → D → E, several levels deep, where understanding class E requires reading four other classes' worth of code first. Usually a sign that composition (covered in its own chapter) should have been used for at least some of those relationships.
Premature/Over-Abstraction — creating interfaces, abstract classes, and design patterns for problems that don't actually need that flexibility yet ('speculative generality'). This adds indirection and complexity without a corresponding benefit — a simple concrete class is often the right starting point, with abstraction added later, only once a genuine, concrete need for it appears.
Tight Coupling via Concrete Dependencies — classes that directly instantiate their own dependencies (new SomeClass() scattered everywhere) rather than receiving them from outside (Dependency Injection, from the SOLID chapter), making the code hard to test and hard to reconfigure.
Overusing Inheritance for Code Reuse — using extends purely to avoid duplicating a few methods, even where no genuine 'is-a' relationship exists, as discussed in the Composition vs. Inheritance chapter.