Design patterns are the shared vocabulary of software engineering. When a senior engineer says "use a strategy here" or "that is a decorator," everyone in the room should understand the shape of the solution without writing a line of code. The original 23 Gang of Four patterns were documented in 1994, and while the landscape changed, roughly 10 of them appear constantly in 2026 codebases. The rest have been replaced by language features, frameworks, or better ideas.
What changed in 2026
- First-class functions deprecated several patterns. Strategy, Command, and Template Method in languages with lambdas are just functions — the class-based ceremony is gone.
- Frameworks absorbed others. Spring, NestJS, and React built Observer, Factory, and Dependency Injection into their cores; you use the patterns without naming them.
- Functional patterns rose. Functor, Monad, and Lens patterns from functional programming are now common in typed TypeScript/Rust codebases.
- Overuse awareness grew. The industry became more explicit about pattern fatigue — not every module needs an Abstract Factory.
The 10 patterns worth knowing in 2026
| Pattern |
Category |
One-line purpose |
| Strategy |
Behavioural |
Swap an algorithm at runtime |
| Observer / Event emitter |
Behavioural |
Notify subscribers of state changes |
| Factory / Factory method |
Creational |
Delegate object construction |
| Builder |
Creational |
Construct complex objects step by step |
| Decorator |
Structural |
Add behaviour without subclassing |
| Adapter |
Structural |
Bridge incompatible interfaces |
| Repository |
Architectural |
Isolate data-access logic |
| Command |
Behavioural |
Encapsulate an operation as an object |
| Proxy |
Structural |
Control access to an object |
| Composite |
Structural |
Treat a tree of objects uniformly |
Strategy — the most useful pattern
Strategy replaces a tangle of if/else or switch with a pluggable algorithm:
interface Sorter<T> {
sort(items: T[]): T[];
}
class QuickSorter<T> implements Sorter<T> {
sort(items: T[]): T[] { /* ... */ return items; }
}
class MergeSorter<T> implements Sorter<T> {
sort(items: T[]): T[] { /* ... */ return items; }
}
class DataProcessor<T> {
constructor(private sorter: Sorter<T>) {}
process(items: T[]) { return this.sorter.sort(items); }
}
In Python or JavaScript, a bare function often replaces the interface class entirely.
Observer — events everywhere
Used directly in React state management, DOM events, RxJS, and Node.js EventEmitter:
class EventBus {
private listeners = new Map<string, Set<Function>>();
on(event: string, fn: Function) {
if (!this.listeners.has(event)) this.listeners.set(event, new Set());
this.listeners.get(event)!.add(fn);
}
emit(event: string, payload: unknown) {
this.listeners.get(event)?.forEach(fn => fn(payload));
}
}
Repository — essential for testability
The Repository pattern abstracts data access behind an interface, letting services remain database-agnostic:
interface UserRepository {
findById(id: string): Promise<User | null>;
save(user: User): Promise<void>;
}
class PostgresUserRepository implements UserRepository { /* ... */ }
class InMemoryUserRepository implements UserRepository { /* for tests */ }
Pair this with dependency injection and your service layer becomes trivially testable.
How to pick
- Multiple interchangeable algorithms or behaviours? → Strategy.
- Something happened and others need to know? → Observer.
- Object construction is complex or conditional? → Factory or Builder.
- Adding behaviour without changing the original class? → Decorator or Proxy.
- Bridging two incompatible interfaces? → Adapter.
- Isolating persistence logic? → Repository.
When in doubt, prefer the simplest code that works. Patterns are applied to a pain point, not prophylactically.
Common mistakes
Singleton for shared services. Singletons create hidden global state, make testing hard, and cause concurrency bugs. Use dependency injection with a single-instance scope instead.
Pattern stacking. Abstract Factory of Strategy Builders wrapped in a Decorator is an architecture diagram, not production code. Each layer must earn its place.
Pattern before problem. Adding a factory before you have two things to construct. YAGNI applies to patterns too.
Using patterns as jargon without intent. Naming something a "factory" when it is just a helper function. Patterns communicate structure; use the name when the structure matches.
What to skip
- Singleton as global state — replace with DI.
- Abstract Factory unless you genuinely need families of related objects (rare outside GUI toolkits).
- Template Method — in 2026, pass a callback or use Strategy with a function; no need for inheritance.
FAQ
Do I need to memorise all 23 Gang of Four patterns?
No. Know the 10 in the table. Recognise the others when you see them. The rest appear rarely outside textbooks.
Are patterns language-specific?
The concepts are universal; the implementations vary. A Strategy in Python may be just a callable. Understand the intent, adapt the form.
Is Repository a Gang of Four pattern?
No — it comes from Domain-Driven Design (Eric Evans, 2003) and Martin Fowler's Patterns of Enterprise Application Architecture. It is now ubiquitous regardless of origin.
How do functional programming patterns relate?
FP patterns like functor (mappable container) and monad (chainable wrapper) solve similar problems to some GoF patterns. They are more composable, less ceremonious, and increasingly common in TypeScript and Rust.
Where to go next
See Dependency injection explained in 2026, Unit testing explained in 2026, and Error handling explained in 2026.