Design patterns are one of those things that separate engineers who've seen some things from those who haven't. They're not magic. They're documented solutions to recurring problems in software design. Learn them once and you'll start recognizing the same patterns everywhere.
The canonical reference is the 1994 book by the Gang of Four: Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. "Design Patterns: Elements of Reusable Object-Oriented Software." Still worth reading.
What are they?
Design patterns are blueprints for structuring code to address common problems. They're not code you copy-paste. They're guidelines proven over decades of real-world use. When you tell another engineer "I'm using a Factory Method here," they immediately understand the approach without reading your code. That shared vocabulary matters.
The three categories
Creational patterns deal with object creation.
Abstract Factory: Creates families of related objects without specifying concrete classes. Good for UI toolkits that need to work across different platforms.
Builder: Constructs complex objects step by step. Useful when an object has many optional parameters.
Factory Method: Defines an interface for creating an object but lets subclasses decide which class to instantiate. Common in frameworks.
Prototype: Creates new objects by cloning an existing one. Use when object creation is expensive and you have a pre-configured instance to copy.
Singleton: Ensures only one instance of a class exists globally. Good for database connections, configuration managers, and similar shared resources.
Structural patterns deal with how objects are composed.
Adapter: Makes incompatible interfaces work together. Wrap an existing class to match the interface your code expects.
Bridge: Separates abstraction from implementation so both can vary independently. Useful for platform-specific code.
Composite: Composes objects into tree structures. Treat individual objects and collections the same way. File systems are the classic example.
Decorator: Adds behavior to objects at runtime without changing their class. Use this instead of subclassing when you need to mix and match behaviors.
Facade: Provides a simplified interface to a complex subsystem. Reduces the API surface that callers need to understand.
Flyweight: Shares common state across many fine-grained objects to save memory. Text editors use this for character objects.
Proxy: Controls access to another object. Useful for lazy loading, access control, and logging.
Behavioral patterns deal with how objects communicate.
Chain of Responsibility: Passes a request along a chain until something handles it. Approval workflows are the obvious use case.
Command: Encapsulates a request as an object. Enables undo/redo, queuing, and logging of operations.
Interpreter: Defines a grammar and an interpreter for it. Used in query languages and expression parsers.
Iterator: Provides a standard way to traverse a collection without exposing its internals.
Mediator: Objects communicate through a mediator rather than directly. Reduces coupling in complex interaction scenarios.
Memento: Captures and restores an object's state. The backbone of undo functionality.
Observer: One object notifies a set of dependents when its state changes. The foundation of event-driven programming and pub/sub systems.
State: An object changes its behavior when its state changes. Good for modeling state machines.
Strategy: Defines a family of algorithms, encapsulates them, and makes them interchangeable. Swap sorting algorithms or pricing rules at runtime.
Template Method: Defines the skeleton of an algorithm in a base class, letting subclasses fill in the steps.
Visitor: Separates an operation from the object structure it operates on. Tax calculations across different product types are the textbook example.
One thing to keep in mind
Patterns are tools, not rules. Don't apply them because you feel like you should. Apply them when they solve an actual problem. The best code is often simple and doesn't need a pattern at all.