Appearance
9.4.1 — Design Patterns: How to Read Them, Derive Them, and Recognize Them
In 1994, four authors — Gamma, Helm, Johnson, Vlissides, forever the Gang of Four (GoF) — published Design Patterns, cataloguing 23 recurring solutions they had observed in real object-oriented systems. The book's deepest contribution was not any single pattern; it was the idea that recurring design solutions deserve names, so that "make the pricing rules a Strategy and register them" transmits an entire design in one sentence.
This folder gives the sixteen patterns that carry almost all real work their own full page each, plus the ecosystem pages the original book could not have written. The other seven patterns are genuinely rare in everyday code, so they are covered for recognition rather than given a page of their own (section 2 explains exactly which, and why). Each pattern page follows the same shape — problem story → derivation → mental model → structure → line-by-line code → scenario gallery → variants → where you already use it → pitfalls → neighbors → interview calibration — because the goal is not that you can recite the pattern. The goal is that when a requirement lands on your desk, the right shape comes to mind on its own, the way a chess player sees a fork without having to calculate it.
This page is the meta-skill: what a pattern is, how patterns are derived rather than memorized, the recognition triggers that fire in daily work, and the failure mode (pattern fever) that gives the topic its bad reputation.
1. What a pattern actually is
A design pattern is a named, reusable arrangement of roles and relationships that resolves a specific recurring tension between forces — flexibility vs simplicity, compile-time vs runtime binding, coupling vs convenience. Three parts matter, and the middle one is the part everyone skips:
- The name — compression for design conversations ("wrap it in an Adapter" = ten minutes of explanation in one word).
- The problem and its forces — when this tension arises. A pattern without its problem is a solution looking for trouble; most pattern misuse is applying the structure where the tension does not exist.
- The structure — the roles (participants), how they point at each other, and what varies vs what stays fixed.
Notice what a pattern is not: a library, a framework, or code to paste. It is a shape. The same pattern looks different in every codebase — Strategy might be an interface with classes, a record of functions, or a Map of lambdas — and recognizing the shape under different clothes is the actual skill.
Everything in Part 9 so far has been secretly building toward this: patterns are 9.1–9.3.1 compiled into reusable machinery. Nearly every GoF pattern is a combination of three moves you already own:
| The move | Where you met it | Patterns built from it |
|---|---|---|
| Isolate what varies behind a role | 9.2.3, 9.3.6 OCP | Strategy, State, Bridge, Abstract Factory, Template Method |
Dispatch replaces selection (polymorphism instead of if/switch) | 9.2.5 | State, Visitor, Chain of Responsibility, Command |
| Composition over inheritance (has-a, wired at runtime) | 9.2.8 | Decorator, Proxy, Composite, Adapter, Facade, Mediator |
If you can derive those three moves, you can re-derive every pattern in this folder from scratch — which is exactly what section 3 trains.
2. The catalog: the sixteen patterns that carry ~99% of real work
The Gang of Four catalogued 23 patterns. Sixteen of them do almost all the work in real codebases. You meet those sixteen every week, they name structures you already build by hand, and they are the ones an interviewer will actually ask about. Each of the sixteen gets its own full page in this folder.
The other seven show up once every few years. They are listed at the end of this section with a plain one-line definition each, so the name never catches you out in a review or an interview. You will not find a dedicated page for them here, because a catalog padded with patterns you use once a decade is what gives this whole topic its bad reputation.
Creational — how objects come into being. "Who calls new, on what, and how many exist?"
- 9.4.2 Factory Method — the which-concrete-class decision, made once, behind a role.
- 9.4.3 Abstract Factory — factories for families of products that must match each other.
- 9.4.4 Builder — stepwise, validated assembly of objects with many optional and interdependent parts.
- 9.4.5 Prototype — new objects by cloning a configured exemplar instead of constructing from parameters.
- 9.4.6 Singleton — exactly-one-instance, taught with its full critique and its modern replacement.
Structural — how objects compose into larger structures. "How do pieces fit together without a coupling explosion?"
- 9.4.7 Adapter — translate a foreign interface into one you define (the anti-corruption layer).
- 9.4.8 Decorator — add behavior by wrapping in a same-interface layer, composed at runtime.
- 9.4.9 Facade — one intent-named interface over a subsystem you must orchestrate.
- 9.4.10 Proxy — a same-interface stand-in that controls access (lazy, caching, protection, remote).
- 9.4.11 Composite — a part-whole tree where leaf and container share one interface.
Behavioral — how objects divide and route behavior. "Who decides, who reacts, and who knows about whom?" (Pages 9.4.12–9.4.17, authored in order: 9.4.12 Strategy, 9.4.13 Observer, 9.4.14 State, 9.4.15 Command, 9.4.16 Chain of Responsibility, 9.4.17 Template Method.)
Beyond the catalog: 9.4.24 maps these patterns onto the JavaScript and TypeScript tools you already use, and adds the catalog of ways people get patterns wrong plus a playbook for choosing between two patterns that look alike.
The other seven, in one line each
These are the names you will hear occasionally without needing a page of practice. Read the seven lines once. From here on, whenever one of these names appears anywhere else in the book, you will already know what it means.
Bridge. You have two things that change for different reasons, and you want to pick each one separately and pair them up however you like. Say you send messages, and there are three kinds of message (text, email, push) and two versions of the sending service (the old one and the new one). Writing a class for every pairing gives you six classes. Bridge means each message kind simply holds a sender, so you pick a message kind and a sender and hand one to the other. Three classes plus two senders, and any pairing works.
Flyweight. You are holding a very large number of objects, and almost all of their contents are identical. So you store the identical part once and let every object point at that one shared copy. You have already benefited from this without writing it: JavaScript engines store one copy of a repeated string rather than thousands (3.6.9).
Mediator. Every component in a screen is talking directly to every other component, so the wiring grows out of control. A mediator is one object in the middle that all of them talk to instead. It knows every component by name and holds the rules, such as "when the country dropdown changes, reload the list of states, then clear the postcode field."
Memento. You save a copy of an object's state so you can put it back later. That saved copy is deliberately dead data — you never run it, you only restore from it. This is how undo works, and 9.4.5 covers the copying machinery it needs.
Visitor. You have a fixed set of shapes — say the node types of a document — and you want to keep adding new operations over them without editing the shapes themselves. You write each operation as a separate object that gets handed every node in turn. TypeScript gives you a lighter way to get the same result, using a union of types and a switch the compiler checks for you (3.7.3).
Interpreter. You represent a small language as a tree of objects, and each object knows how to evaluate itself. This is not rare so much as specialised — and it gets a whole working chapter rather than a footnote, because 3.11 builds one end to end.
Iterator. Not rare at all. It is so fundamental that the language swallowed it whole: for…of, Symbol.iterator and generators are this pattern, and 3.6.6 covers it in full.
3. How patterns are derived (the method this folder uses on every page)
A pattern you memorized is trivia; a pattern you derived is judgment. Every pattern in this folder is presented by walking the derivation the original authors walked — and the walk always has the same five steps. Learn the walk once, and you can reconstruct any pattern you have forgotten, or invent the right one for a tension nobody has named yet.
Step 1 — Write the naive code. Start with the simplest thing that works: one class, one if, one hard-coded new. This is not a strawman; it is genuinely the correct design until a specific force arrives.
Step 2 — Apply the force. A requirement changes. A second payment provider. A second export format. A second tenant. Watch precisely what breaks in the naive code — and name the breakage using 9.1 vocabulary: shotgun surgery (one change, many files), divergent change (one file, many reasons), rigidity, or untestability.
Step 3 — Draw the varies/fixed line. This is the pivot of every pattern. Ask: what part of this code changed when the force arrived, and what part stayed identical? The changing part becomes a role (an interface, a function type, an object). The stable part becomes the context that holds the role.
Step 4 — Choose where the role is bound. Compile time (inheritance/subclassing) or run time (composition/injection)? The answer forks the catalog: Template Method vs Strategy, Factory Method vs Abstract Factory, Adapter vs Bridge. Modern default is runtime composition, and each page states when it is not.
Step 5 — Name it, then name its cost. Every pattern worsens an axis to improve another. Decorator makes "add a layer" cheap and "see all behavior in one place" expensive. Observer makes "add a reaction" cheap and "trace the flow" expensive. If you cannot state the cost, you have not finished the derivation — and in a design review, the cost is the sentence that buys you credibility.
The one-sentence version
Naive code → a force arrives → something varied and something didn't → give the varying part a name and a home → say what the new home costs. That is the entire pattern movement, and every one of the 23 is an instance of it.
4. Recognition: the triggers that fire in daily work
Deriving is for design time. Recognition is for the other 95% of the time — when a requirement is being described in a meeting, a ticket is being read, or an interviewer is talking. This table is the muscle memory. Read the left column as sentences people actually say; the right column is what should surface in your head before they finish the sentence.
| When you hear / read … | The shape that should surface | Page |
|---|---|---|
| "depending on the type / provider / region, create a different …" | Factory Method | 9.4.2 |
| "the whole set has to match — you can't mix a Stripe client with a PayPal webhook verifier" | Abstract Factory | 9.4.3 |
| "the constructor now takes nine arguments and half are optional" | Builder | 9.4.4 |
| "copy this configured object and tweak two fields" · "duplicate the template" | Prototype | 9.4.5 |
| "there must be exactly one connection pool / config / cache" | Singleton (and its replacement) | 9.4.6 |
| "their SDK's interface doesn't match what our code expects" | Adapter | 9.4.7 |
| "add retry, then caching, then logging — in any combination, at runtime" | Decorator | 9.4.8 |
| "the client shouldn't need to know about these six subsystems" | Facade | 9.4.9 |
| "check permission / lazy-load / cache / rate-limit before the real call" | Proxy | 9.4.10 |
| "a folder contains files and folders" · "a team contains members and sub-teams" | Composite | 9.4.11 |
| "the algorithm varies but the caller shouldn't care which" | Strategy | 9.4.12 |
| "when X happens, these five unrelated things should react" | Observer | 9.4.13 |
| "the object behaves completely differently depending on its status" | State | 9.4.14 |
| "we need undo, or a queue of pending actions, or an audit log of what was requested" | Command | 9.4.15 |
| "try handler A, then B, then C, until one handles it" | Chain of Responsibility | 9.4.16 |
| "same steps every time, but step 3 differs per subclass" | Template Method | 9.4.17 |
| "iterate this without exposing how it's stored" | Iterator — language feature | 3.6.6 |
| "we need this across two independent axes — {SMS, email} × {v1, v2}" | Bridge — rare | (section 2) |
| "we're holding two million objects that are 95% identical" | Flyweight — rare | (section 2) |
| "every component talks to every other component" (n² wiring) | Mediator — rare | (section 2) |
| "restore the document to how it was before" | Memento — rare | (section 2) |
| "add a new operation over a fixed set of node types without editing them" | Visitor — rare | (section 2) |
Read that table three times and you have bought yourself the single highest-leverage minute in this folder. The pages then give you the why behind each row so the recognition is grounded, not reflexive.
5. How to study one pattern so it sticks
The standard failure is studying patterns as UML flashcards — structure memorized, problem forgotten, applied wherever the structure can fit rather than where it should. The six-question protocol every page in this folder answers, and that you should demand of yourself before using one:
- What tension does it resolve? No tension present = no pattern needed. (Strategy resolves "an algorithm varies per context and callers should not care which"; if the algorithm does not vary, a plain function is the design.)
- What varies, and what is fixed? Every pattern draws exactly this line. Naming it tells you which future changes become cheap — and which become expensive.
- Where do I already use it? Every pattern in the catalog is already in your daily stack:
express.use(Chain),JSON.parse's reviver (Strategy),fs.createReadStream(…).pipe(gzip)(Decorator on streams), your ORM's.where().limit()(Builder), every event listener (Observer),Symbol.iterator(Iterator). Recognition in the wild is the difference between vocabulary and trivia. - What is the minimal honest version in my language? In TypeScript, much GoF machinery compresses: a Strategy is often a function type, a Command is a closure (3.6.2), a Factory is a function returning an interface. The pattern is the role structure, not the class count.
- What would misuse look like here? If you cannot articulate the misuse, you do not yet own the pattern.
- What does it cost? Named in step 5 of the derivation. Every page ends with it.
6. Pattern fever: the anti-pattern about patterns
The disease named in the community since the late 1990s: fresh pattern knowledge demands application, and codebases sprout names like AbstractSingletonProxyFactoryBean (a real Spring class, and the eternal punchline). Three mechanisms, so you can catch them in yourself and in review:
- Structure without tension — a plug point where nothing varies (9.3.6's OCP caveat), an Adapter between two interfaces you both own (just change one), a Singleton for something that could be a parameter.
- Resume-driven complexity — the pattern as proof of sophistication. Tell: the PR description names patterns instead of problems.
- Heavyweight spelling — five classes where the language offers a one-line equivalent. GoF wrote for a C++/Smalltalk world without first-class functions; TypeScript is not that world.
The countermeasure is the direction of reasoning from 9.3.1: from problem to pattern, never pattern to problem. In review vocabulary: every pattern introduction must name the demonstrated tension it resolves, the same way every abstraction must name its stakeholder. And the reassurance worth internalizing: not using a pattern is often the senior move — the design that solves today's problem in twenty plain lines beats the one that solves five hypothetical problems in two hundred.
7. The expert lens
Patterns are a shared language before they are designs. Their highest-value use is communication: architecture discussions, code review, interviews, and reading unfamiliar code all accelerate when both sides hold the vocabulary. This is why interviews probe patterns even at companies whose codebases use few — they are testing whether you can name what you are doing. A candidate who says "I would start with a plain function and promote it to Strategy when the second algorithm arrives" signals more seniority than one who opens with a class diagram.
The catalog is a snapshot of one paradigm's workarounds — and that is instructive. Many GoF patterns are visible compensation for what 1994 languages lacked: Strategy and Command compensate for missing first-class functions; Iterator for missing generators (3.6.6 — the language absorbed it outright); Prototype for rigid ways of creating objects from classes (3.6.4 — JavaScript is this pattern); Visitor for missing pattern matching. Peter Norvig's observation — that most GoF patterns are "invisible or simpler" in dynamic languages — is the lens: when a pattern dissolves into a language feature, learn the feature; when it survives every language (Adapter, Facade, Observer, Strategy-as-role, Composite), it is naming something essential about design, not about language gaps. Both kinds get a page here, because the dissolved ones are exactly where interviewers probe whether you understand your own language deeply.
Patterns scale up. Adapter reappears as the anti-corruption layer between services; Facade as the API gateway; Observer as pub/sub messaging; Chain of Responsibility as middleware pipelines; Proxy as sidecars and reverse proxies; Mediator as the orchestrator in a saga (10.8.4). Learning them at class scale is learning distributed-architecture vocabulary at the cheapest possible tuition — one more reason LLD precedes HLD in this book.
Next: creation begins — 9.4.2: the which-concrete-class decision, derived from a two-line if that grew into a company-wide problem.
Recall
- A design pattern = name + problem/forces + role structure — a named resolution of a recurring design tension. The problem half is the part that matters most: structure applied without its tension is the definition of misuse. Patterns = 9.1–9.3.1 compiled into reusable shapes, built from three moves: isolate what varies · dispatch replaces selection · composition over inheritance.
- The roster is the sixteen high-frequency patterns, one full page each: creational (Factory Method, Abstract Factory, Builder, Prototype, Singleton), structural (Adapter, Decorator, Facade, Proxy, Composite), behavioral (Strategy, Observer, State, Command, Chain of Responsibility, Template Method). The rare five (Bridge, Flyweight, Mediator, Memento, Visitor) are covered for recognition only, and Iterator lives at 3.6.6 because the language absorbed it. Plus the JS/TS ecosystem map, the anti-pattern catalog, and the selection playbook.
- Derivation, the five steps: naive code → apply the force → draw the varies/fixed line → choose compile-time or runtime binding → name it and name its cost. A pattern whose cost you cannot state is not yet derived.
- Recognition triggers (section 4) are the daily-work skill: sentences in requirements map to shapes before the sentence finishes. "Their SDK doesn't match our interface" → Adapter. "Behaves differently by status" → State. "Undo / queue / audit" → Command.
- Pattern fever: structure without tension, resume-driven complexity, heavyweight spelling. Cure = 9.3.1's direction of reasoning: problem → pattern, never the reverse. Patterns scale: Adapter→anti-corruption layer, Facade→API gateway, Observer→pub/sub, CoR→middleware, Proxy→sidecar.
Self-test: Give the three parts of a pattern and say which one misuse always omits. Recite the five derivation steps. Name the question each GoF category answers. Give five recognition triggers from memory with their patterns. Name three patterns that dissolved into language features and three that survive everywhere.
Quiz Bank
FoundationalWhat is a design pattern, and why is the problem half more important than the structure?
A named, reusable arrangement of roles that resolves a specific recurring tension (forces) in design — three parts: name (conversation compression), problem/forces (when the tension exists), structure (participants, and what varies vs stays fixed). The problem half dominates because the structure alone is unfalsifiable — almost any structure can be imposed anywhere, and imposing it where the tension is absent adds indirection with no offsetting benefit (accidental complexity, 9.1 section 5). That is precisely the mechanism of pattern fever: UML memorized, forces forgotten, Strategy applied to an algorithm that never varies. Correct direction of reasoning, as with SOLID (9.3.3): from demonstrated problem to pattern, never the reverse — and in review, a pattern's introduction is justified by naming its tension, not its name.
FoundationalRecite the five-step derivation and apply it to any pattern of your choice.
(1) Write the naive code — the simplest correct thing, which is genuinely right until a force arrives. (2) Apply the force — a requirement changes; watch exactly what breaks and name the breakage in 9.1 terms (shotgun surgery, divergent change, rigidity, untestability).
(3) Draw the varies/fixed line — what changed vs what stayed identical; the changing part becomes a role, the stable part becomes the context holding it. (4) Choose the binding time — compile time (subclassing) or runtime (composition/injection); this fork separates Template Method from Strategy, Factory Method from Abstract Factory.
(5) Name it and name its cost — every pattern worsens one axis to improve another. Worked on Decorator: naive = a HttpClient that fetches; force = "add retry", then "add caching", then "add request logging", in different combinations per call site; breakage = each concern added inside the client makes one class hold four reasons to change (divergent change) and combinations explode via subclassing (2ⁿ classes); varies/fixed = the extra behavior varies, the interface is fixed; binding = runtime composition so combinations are assembled per call site; name = Decorator, cost = the behavior of a wrapped object is no longer visible in one place, and stack traces/debugging pass through layers. Being able to run these five steps on an unfamiliar pattern is what makes the catalog re-derivable rather than memorized.
AppliedNorvig observed most GoF patterns become invisible or simpler in dynamic languages. Explain with three concrete dissolutions and say what remains pattern-shaped anyway.
Many GoF structures compensate for missing 1994-language features, so richer languages absorb them. Strategy in C++ needs an interface plus classes; with first-class functions it is a function-typed parameter — sort(xs, (a, b) => …) is Strategy with zero ceremony (3.6.2).
Iterator required an object protocol hand-built per collection; JavaScript absorbed it into the language — Symbol.iterator, generators, for…of (3.6.6). Prototype was a workaround for rigid ways of creating objects from classes; JavaScript's object model is prototypal, and structuredClone covers the deep-copy case natively (3.6.4). What survives every language: patterns about relationships between independently-owned parts — Adapter (you do not control both interfaces), Facade (subsystem complexity is real regardless of syntax), Observer (decoupled reaction is architectural), Composite (part–whole recursion is domain shape), Mediator (n² wiring is a topology problem, not a syntax problem).
Rule extracted: when a pattern dissolves into a feature, learn the feature and keep the name for design conversations; when it survives, it names something about design itself, and it will reappear at service scale (Part 10). Both categories are covered in this folder for that reason — the dissolved ones double as deep-dives into your language.
InterviewHow do you decide whether to use a pattern's full class-based spelling or a lightweight functional one? Give criteria with an example both ways.
The pattern is the role structure; the spelling should be the cheapest one that makes the roles visible and swappable in context. Criteria for the heavyweight (interface + classes) spelling: the role carries state or multiple cohesive methods (a Carrier with rate/label/track — 9.3.6's OCP example); implementations need construction-time dependencies of their own (a strategy that itself needs a database client composes better as a class with DI); the set benefits from registry plus exhaustiveness typing (Record<Kind, Impl> — 3.7.3); or the codebase's navigation conventions lean on named classes. Criteria for the functional spelling: the role is one method and stateless — then a function type is the interface: type PricingRule = (price: Money, ctx: Ctx) => Money, a Map of lambdas is the registry, and test doubles become inline arrows.
Example both ways: retry policy as interface RetryPolicy { nextDelay(attempt): ms; shouldRetry(err): boolean } (two cohesive methods, configuration state) versus debounce-wait selection as (ctx) => ms (one decision, no state). Anti-criteria worth naming aloud: choosing classes to look like the book is pattern fever; choosing lambdas everywhere until a genuinely two-method role gets smeared across parallel Maps is the mirror-image failure.
StaffYour organization's interview loop and design docs are pattern-vocabulary-heavy, but the codebase is a plain, well-factored functional-core TypeScript system with few named patterns. A candidate-turned-hire challenges: why did you grill me on patterns you do not use? Give the principled answer and what it implies for how the org should teach and review.
The honest answer has three layers. (1) The vocabulary is in use — under different spellings. The codebase's function-typed dependencies are Strategies; its ports (9.3.9 DIP) are role abstraction; its middleware is Chain of Responsibility; its emitters are Observer; its pipe() chains are Decorator. Pattern knowledge was probed because it is the portable name for structures the org genuinely lives in — the interview tested recognition-under-different-clothes, which is the transferable skill, not class-diagram recital. (2)
Patterns are the industry's inter-team protocol. Design docs, vendor discussions, incident reviews, and future codebases (including acquisitions and open source) speak this language; an engineer who cannot map "we should decorate the client with retry plus tracing" to and from code is slower everywhere that matters, regardless of local style. (3)
The direction-of-reasoning filter was the real test: strong candidates answered pattern questions by naming tensions and minimal spellings ("that is a function type until the second method shows up") — exactly the judgment the plain codebase embodies. Implications for the org: teach patterns as recognition training over its own code (a "pattern census" document mapping local idioms to their standard names — cheap, high-value onboarding); require design docs to name tension-then-pattern, never pattern-first; keep interviews probing mapping in both directions ("here is our functional retry wrapper — name it, critique it, and say when you would promote it to classes"). The meta-principle: a pattern-free-looking codebase built by pattern-literate engineers is the success state — the vocabulary's job is to disappear into good structure and reappear whenever humans must discuss that structure.
Flashcards
FlashPattern = three parts
Name (compression) + problem/forces (when) + role structure (what varies vs fixed). Misuse always drops the middle part.
FlashThe five derivation steps
Naive code → apply the force → draw the varies/fixed line → pick compile-time or runtime binding → name it and name its cost.
FlashCategory questions
Creational: who news what, and how many? Structural: how do pieces fit? Behavioral: who decides, who reacts?
FlashThe three moves under all 23
Isolate what varies behind a role · dispatch replaces selection · composition over inheritance. Every pattern is a recombination.
FlashPattern fever tells
Structure without tension; PR names patterns not problems; five classes where a lambda serves. Cure: problem → pattern, never reverse.
FlashPatterns at HLD scale
Adapter→anti-corruption layer · Facade→API gateway · Observer→pub/sub · CoR→middleware · Proxy→sidecar · Mediator→saga orchestrator.
Scenario Drill
DrillA junior teammate returns from a patterns course and opens a PR refactoring your working payment-notification module: an AbstractNotificationFactory producing NotifierSingletons, each wrapped in a LoggingDecoratorProxy, dispatched through a NotificationCommandBus — for a system with one channel (email) and no roadmap for more. Review it constructively: what do you reject, what do you salvage, and how do you turn this into a teaching moment that does not kill their enthusiasm?
Reject, with the tension test applied per pattern. Abstract Factory resolves "families of related products vary together" — there is one product and one family; no tension, pure indirection (9.4.3). Singleton adds global reachability and hidden coupling to something the existing constructor injection already scoped correctly — this one worsens the design rather than merely padding it (9.4.6). Decorator-plus-Proxy for logging: the tension (layerable cross-cutting concerns in multiple combinations) is absent with one concern and one channel; a log line inside the adapter, or at most one wrapper when the second concern arrives, is the honest size. CommandBus: requests-as-objects pay for queues, undo, audit, and scheduling — none requested; today it is a function call wearing a costume. Collectively: every structure can fit; no structure has its problem — the definition of pattern fever (section 6).
Salvage — genuinely. The junior correctly identified the seams: channel dispatch, cross-cutting concerns, and request representation are exactly where variation would attach if it came. Point out that their instincts about where flexibility belongs were right; the error was when (before demonstrated need) and at what weight (class ceremony where the language offers lighter spellings).
The teaching move: do not just decline — redirect the energy into the two artifacts that make pattern knowledge compound. (1) Have them write the team's pattern census: map existing idioms to their standard names (our middleware = Chain of Responsibility, our port injection = Strategy/DIP, our stream pipelines = Decorator) — recognition training that makes course knowledge land on real code. (2) Agree on promotion triggers in the module's README: "second channel → registry plus factory; second cross-cutting concern → decorator chain; queuing or audit requirement → command objects." Now the patterns sit exactly where they belong — as named future moves with defined triggers — the junior's course became team documentation, and the codebase stayed twenty lines. Close with the sentence that reframes seniority for them:
knowing the pattern is the entry fee; knowing its tension, its minimal spelling, and its trigger is the craft.