Software patterns
resolve forces.
A field guide to the 1994 design patterns—translated into JavaScript, Python, Ruby, C, Rust, Java, Go, C#, and TypeScript, re-evaluated after three decades of language evolution.
Patterns are an attempt to write down good things we build repeatedly.
Especially those things that are not immediately obvious: that is, not yet part of our current toolkit.
Patterns embody the repeatable, reasonable ways we meet our needs while avoiding problems -- in a general a way, where the specifics aren't as important as the overall idea.
The idea is not to "use lots of patterns".
It's to be aware of patterns, and whole pattern languages, so you can choose to use them, or not.
need to be identified
A pattern helps with recurring pressures.
If you can't name the pressures, using a pattern is less enlightening.
If language features already resolve these pressures, use them.
Programming languages are, in general, low-level pattern languages.
But we want to name old and new solutions, to old and new conflicts.
This helps us to better expand our individual, and shared, expression and reasoning about computing.
A pattern is not code you copy.
It is a recurring relationship among a context, a problem, competing forces, and a solution abstraction that will take many concrete forms. That is why the same GoF pattern looks class-heavy in 1994 C++, function-heavy in JavaScript, trait-heavy in Rust, and like structs plus function pointers in C.
From Christopher Alexander to software — in small steps.
The software pattern movement did not borrow “pattern” to mean a decorative motif. Alexander’s architectural patterns describe recurring situations where several needs pull in different directions. The solution is a relationship that resolves those wicked pressures without prescribing one identical structure every time.
Where should the gate go?
Watch use reveal a recurring relation; then reinforce it.
What aged well—and what changed?
The vocabulary entered mainstream frameworks and libraries; Fowler called the book enormously influential and pointed specifically to Java/.NET libraries.
A systematic review of 50 primary studies and a mapping study of roughly 120 report conflicting results and substantial measurement/context problems.
Iterator, callbacks, closures, events, traits, sum types, pattern matching, modules, and generators absorb much of the old ceremony. The pressure remains; the implementation shrinks.
Therefore the ratings below are an editorial synthesis, not popularity statistics. “Core” means the underlying move remains broadly useful; “Native” means modern language/library features often embody it directly; “Contextual” means strong only when its specific forces exist; “Caution” means the classic implementation can create as many problems as it solves.
The 23 at a glance.
Filter by family or survival status. Click any tile to jump to its full explanation.
One pattern at a time.
Every pattern has four levels: a human situation, a software situation, the original GoF move, and its modern translation. The code selector changes every card so you can read the whole catalog in one language, or compare all nine on any pattern.
The same pressure, different language.
Dynamic languages
JavaScript, Python, and Ruby can often replace “one class per variation” with first-class functions, closures, duck typing, modules, generators, and runtime delegation. This was the thrust of Norvig’s famous critique: a pattern can be evidence of missing language support.
Java / C#
Modern Java and C# kept the object vocabulary but added lambdas, records, events/delegates, streams, pattern matching, and richer concurrency APIs. Classic class-heavy implementations can often be compressed without losing the design relation.
Rust
Traits can express Strategy/Bridge-like variation; enums plus exhaustive matching can replace some State/Visitor machinery; ownership and typestate can encode constraints the original book had to enforce dynamically.
C and Go
C reveals that these patterns were never fundamentally “about classes”: structs, opaque handles, function pointers, and explicit context are enough. Go likewise tends toward small interfaces and functions; its newer iterator protocol is a striking example of pattern pressure becoming standard language/library vocabulary.
Glossary.
Hover any underlined technical term in the prose; the same definitions live here for scanning.
Sources and further reading.
The explainer deliberately mixes primary history, language documentation, practitioner interpretation, and empirical software-engineering research. Source links are external; the explainer itself has no runtime dependencies.