skip to content

How do you evaluate the claim that design patterns are just workarounds for missing language features, and what does that mean for standards in a polyglot organisation?

level: principalimportance: nice to knowfreq 20%

answer

  1. Norvig: 16 of 23 invisible/simpler in Lisp
  2. absorbed = mechanism patterns; untouched = structural + distributed
  3. pattern = problem + forces + consequences, not a class diagram
  4. shared intent doc, per-language style guide
  5. prune standards as languages evolve

basics

~20 s

Partly true: some patterns exist only because a language lacked a feature (Strategy without first-class functions, Visitor without pattern matching) and vanish once it gains one. But patterns also name problems and trade-offs, which no feature removes. Treat them as shared vocabulary, not code templates.

solid answer

~60 s

The claim, argued most famously by Peter Norvig, is that many Gang of Four patterns are invisible or trivial in more expressive languages. It holds for the mechanism-heavy ones: Strategy, Command and Template Method reduce to first-class functions; Visitor reduces to pattern matching with exhaustiveness checking; Singleton reduces to modules or DI scopes; Iterator reduces to generators; Null Object reduces to option types. It fails as a general theory because the durable part of a pattern is not its class diagram but its statement of a recurring problem, the forces in tension, and the consequences of resolving them one way. Adapter, Facade, Composite, Circuit Breaker and Repository are not missing keywords. Organisationally the implication is concrete: publish intent-level vocabulary that crosses languages, push mechanism-level guidance into per-language style guides that follow each language's idioms, and treat pattern names as communication compression in reviews and ADRs rather than as a checklist to satisfy. The failure mode to police is cargo-culting a 1994 class structure into a language that no longer needs it.

go deeper

for a junior

Say some patterns disappear when a language has better features — for example Strategy becomes just passing a function — but the underlying problems remain.

for a middle

Give a concrete list of absorbed patterns and a counter-list (Adapter, Facade, Composite) that no feature removes, and note pattern names remain useful vocabulary.

for a senior

Argue both sides explicitly, distinguish a pattern's implementation from its problem-plus-consequences content, and mention that new distributed/cloud pattern catalogues keep appearing.

for a principal

Turn it into policy: layer standards into intent-level (shared) and mechanism-level (per language), write guidance as forces rather than templates, review for both over-patterning and unidiomatic implementation, and prune standards as languages evolve.

## The claim and its strongest form Peter Norvig's much-cited observation (from a 1996 talk analysing the GoF catalogue against dynamic languages) is that **16 of the 23 GoF patterns are 'invisible or simpler'** in Lisp or Dylan — they disappear into first-class functions, multiple dispatch, macros, or dynamic typing. Paul Graham made a sharper version: patterns are a sign the language is not powerful enough, and a recurring pattern is duplication your language cannot let you abstract away. The evidence for it is strong for the mechanism-heavy patterns: | Pattern | Absorbed by | |---|---| | Strategy, Command, Template Method (hooks) | first-class functions and closures | | Visitor | pattern matching over sum/algebraic types, multiple dispatch, exhaustiveness checking | | Iterator | generators, coroutines, built-in iteration protocol | | Singleton | module-level values, `object` declarations, DI-container lifetime scopes | | Null Object | option/maybe types, null-safe operators | | Abstract Factory (simple) | supplier functions, constructor references | | Decorator (single operation) | function composition, higher-order wrappers, delegation syntax | | Prototype | built-in structural copy / clone, immutable `copy(with = ...)` | Over thirty years the trend is real: languages watch which patterns people keep writing and grow features that make them unnecessary. That is *how language design works*. ## Where the claim breaks down 1. **A pattern is not its implementation.** The durable content of a pattern entry is *problem + context + forces + consequences*, and a keyword cannot supply that. Even in a language where Strategy is a lambda, the question "is behaviour here actually varying, and along which axis?" is a design decision that must still be made and communicated. 2. **Many patterns are structural, not syntactic.** Adapter exists because two independently evolved interfaces disagree — no language feature removes external APIs. Facade exists because a subsystem is genuinely complex. Composite exists because a domain is recursive. Mediator, Repository, Circuit Breaker, Bulkhead, Saga, Outbox, Anti-Corruption Layer are all about system realities, not missing syntax. 3. **Vocabulary has independent value.** "Wrap this in a Circuit Breaker" or "that's an Anti-Corruption Layer" compresses a paragraph into two words in a design review. The compression survives even where the code is trivial. 4. **New patterns keep appearing** at levels the language cannot reach: distributed systems (Saga, Outbox, Idempotency Key, Leader Election), cloud (Sidecar, Ambassador, Strangler Fig), and data (CQRS, Event Sourcing). Language expressiveness does not touch these. 5. **The catalogue also contains cautionary entries.** Singleton is largely regarded as harmful; the honest reading of the claim is "some catalogued items were never good patterns", not "patterns as a genre are obsolete". ## The balanced position > Patterns are **descriptions of recurring problems and the trade-offs of their solutions**. Their implementation cost is language-dependent and shrinks as languages evolve; their communicative and analytical value is language-independent. Judging a pattern by how many classes it needs is judging the wrong thing. ## What this means for a polyglot organisation 1. **Separate the three layers in your standards.** - *Cross-cutting, intent-level* (design and architectural patterns, plus their consequences and when not to use them) → one shared document/ADR corpus. - *Per-language mechanism* (how Strategy is spelled in Kotlin vs Go vs TypeScript) → that language's style guide, written in that language's idioms. - *Framework specifics* → owned by the platform team. Mixing them produces the classic failure: Java-shaped code in Go, class-heavy ceremony in Python. 2. **Write guidance as forces, not templates.** "Use Strategy when the algorithm varies per tenant and both variants must be independently testable" transfers across languages; "create an interface plus one class per algorithm" does not. 3. **Police both failure directions in review.** Over-patterning (indirection with no varying axis, five types where a function suffices) and under-idiom (a pattern implemented against the grain of the language). 4. **Let idioms be locally sovereign.** Each language community's idioms are the accumulated efficiency of thousands of engineers; overriding them for cross-language uniformity buys consistency you rarely need and costs fluency you always need. 5. **Re-examine standards when the language moves.** Records/data classes, sealed hierarchies and pattern matching, coroutines, `using`/`defer`, and DI containers each retire specific guidance. Standards documents that never shrink become archaeology. 6. **Keep the vocabulary alive where it pays.** Pattern names belong in ADRs, review comments and onboarding — they are compression. They do not belong as mandatory checkboxes in a design template. ## How to answer Steel-man the claim with concrete absorbed patterns, then bound it: patterns are problem descriptions with consequences, structural and distributed patterns are untouched by syntax, and new pattern catalogues keep forming above the language level. Land on the organisational implication — intent-level vocabulary shared, mechanism-level guidance per language, and standards that get pruned as languages evolve.

  • Name patterns no language feature could plausibly absorb, and say why.
    Adapter and Anti-Corruption Layer (they exist because external interfaces disagree with yours), Facade (subsystem complexity is real), Composite (the domain is recursive), and the distributed set — Circuit Breaker, Bulkhead, Saga, Outbox, Idempotency Key — which address partial failure and network semantics that no syntax removes.
  • How would you word a cross-language standard so it survives translation?
    State the forces and the decision rule, not the mechanism: 'when a policy varies per tenant and each variant needs independent tests, inject it as a named, replaceable dependency', letting each language's style guide specify whether that is a lambda, an interface, a trait object or a module.
  • What signals that your pattern guidance has gone stale?
    Guidance mandating structures the language now provides — hand-rolled iterators where generators exist, Visitor where pattern matching with exhaustiveness checks exists, Singleton classes where DI scopes or module objects exist — and a standards document that has only ever grown, never been pruned.

saying these in an interview costs you the question

  • Accepting the claim wholesale: 'patterns are obsolete, just use a better language'.
  • Rejecting it wholesale and mandating GoF class structures in every language.
  • Confusing pattern implementation cost with pattern value — judging by class count.
  • Ignoring that new pattern catalogues (distributed, cloud, data) keep forming above the language level.
  • Enforcing one cross-language mechanism standard, producing unidiomatic code in every language it touches.
  • Never pruning standards after the languages gain the features that retire them.

context