Skip to content

The rest of the GoF catalogue at a glance

Know the handful in your stack, name the rest by the problem each solves, and use one only where that problem hurts.

Predict first

Before you read: predict

ExamHall's exam page has a countdown timer, an autosave and a status banner. The page creates one ExamSessionStore and works through the parts it holds. The files are below.

Which pattern is ExamSessionStore?

Code for this question
// exam-session.tsexport interface ExamSession { report(from: string, what: string): void; } // parts.tsimport { ExamSession } from './exam-session';export class Timer {  constructor(private session: ExamSession) {}  expire(): void { this.session.report('timer', 'expired'); }  pause(): void {}}export class Autosave {  constructor(private session: ExamSession) {}  fail(): void { this.session.report('autosave', 'failed'); }  flush(): void {}}export class Banner {  constructor(private session: ExamSession) {}  show(text: string): void {}  retry(): void { this.session.report('banner', 'retry'); }} // exam-session-store.tsimport { ExamSession } from './exam-session';import { Timer, Autosave, Banner } from './parts';export class ExamSessionStore implements ExamSession {  timer = new Timer(this);  autosave = new Autosave(this);  banner = new Banner(this);  report(from: string, what: string): void {    if (from === 'timer' && what === 'expired') { this.autosave.flush(); this.banner.show('Time is up: submitting'); }    if (from === 'autosave' && what === 'failed') { this.banner.show('Offline: answers kept on this device'); this.timer.pause(); }    if (from === 'banner' && what === 'retry') this.autosave.flush();  }}
Pick one

The 30-second version

Seven of the 23 GoF patterns are mostly for recognising. Several share one shape, so name each by its problem: copy a configured object (Prototype), share unchanging state (Flyweight), split two independent dimensions (Bridge), route peers through one hub (Mediator), keep an object's own snapshots (Memento), keep operations outside a structure (Visitor), evaluate a small language (Interpreter).

Know the handful in your stack, name the rest by the problem each solves, and use one only where that problem hurts.

How it works

Step 1 of 11

A map, not a syllabus. The GoF book names 23 patterns; refactoring.guru's catalogue lists 22, grouped as creational, structural and behavioral.
In the kitchen
  • the model book lists hundreds of models; a folder knows the bases her own models start from and names the rest when she sees them → a map of names, not a syllabus (step 1)

Analogy: An origami folder and the model book (paper folding)

At the folding table In code
the model book lists hundreds of models; a folder knows the bases her own models start from and names the rest when she sees them a map of names, not a syllabus (step 1)
two models can begin from the same base and the same first folds; what the last folds are for tells them apart name the intent, not the shape (step 2)
a forty-fold model folded to hold a napkin that one fold would hold a pattern used without its problem (steps 10–11)

In real terms: reading code, ask what problem a group of classes solves before asking what they look like; the name follows from the problem.

Breaks down when: a fold's purpose shows in the paper, while a class's intent may live only in its author's head and its commit message.

Contrasting case: someone folding one paper aeroplane needs no names at all; a one-off script needs no patterns.

Examples, run for real

4.1 ExamHall: three parts wired to each other

EXH-4870: ExamHall's exam page wired its timer, autosave and banner to each other: the timer's expiry flushed the autosave and set the banner, a failed save set the banner and paused the timer, and the banner's retry flushed the autosave. Practice mode wanted the timer alone.

MR !5170 gives each part an ExamSession to report to, and ExamSessionStore decides who reacts (t1's code; t1 is answered before this section):

4.1 EXH-4870 as shipped: part files importing another part: 3 of 3 (5 imports) · loading timer.ts for practice mode also loads autosave.ts, banner.ts
4.1 MR !5170: part files importing another part: 0 of 3 · loading timer.ts loads exam-session.ts only · practice-session.ts imports timer.ts of the parts
4.1 both versions: time up -> flushed 1, timer running, banner "Time is up: submitting" · save fails -> flushed 1, timer paused, banner "Offline: answers kept on this device" · retry -> flushed 2, timer paused, banner "Offline: answers kept on this device"

The page behaves the same; the parts now depend on one interface, so practice mode reuses the timer with its own session (step 6).

4.2 ExamHall: the rubric-import codemods

EXH-4871: two codemods for imported rubrics each walked the rubric tree by hand. The second walked only one level below each section, so criteria inside a group went uncounted.

MR !5171 keeps one walk and makes each codemod an object with a method per node type; the walk, not the node, picks the method by the node's type:

// rename-points.ts, MR !5171
export const renamePoints: Visitor = {
  criterion(n) { n.maxScore = n.points; n.points = undefined; },
};
4.2 EXH-4871 as shipped: 2 codemods, 2 hand-written walks · final-2026 rubric, 4 criteria inside groups: criteria 14 · renamed 14 · counted 10
4.2 MR !5171: 1 walk, 3 visitor objects (strip-drafts.ts added as one file; tree.ts unchanged) · criteria 14 · renamed 14 · counted 14

A third codemod is one new object, and no node type changes (step 8).

Try it

Answer first, then check. Nothing is saved.

Question 1 of 7

A team chat draws emoji reactions under messages. A busy channel's history holds 50,000 reactions using 40 different emoji. The files are below.

Which pattern are EmojiArt and emojiArt()?

Code for this question
// emoji-art.tsexport class EmojiArt {  constructor(readonly name: string, readonly pixels: Uint8Array) {}  draw(x: number, y: number): string { return `${this.name} at ${x},${y}`; }}const loaded = new Map<string, EmojiArt>();export function emojiArt(name: string): EmojiArt {  let art = loaded.get(name);  if (!art) { art = new EmojiArt(name, new Uint8Array(64 * 64)); loaded.set(name, art); }  return art;} // reaction.tsimport { EmojiArt, emojiArt } from './emoji-art';export class Reaction {  readonly art: EmojiArt;  constructor(name: string, readonly x: number, readonly y: number) { this.art = emojiArt(name); }  render(): string { return this.art.draw(this.x, this.y); }}
Pick one

Question 2 of 7

A team chat's message box can take a draft back to an earlier version. The files are below.

Which pattern are save, restore and the objects DraftHistory keeps?

Code for this question
// composer.tsexport interface Saved { readonly at: number; }class ComposerState implements Saved {  constructor(readonly at: number, readonly text: string, readonly cursor: number) {}}export class Composer {  private text = '';  private cursor = 0;  type(s: string): void { this.text += s; this.cursor = this.text.length; }  save(at: number): Saved { return new ComposerState(at, this.text, this.cursor); }  restore(s: Saved): void { const st = s as ComposerState; this.text = st.text; this.cursor = st.cursor; }} // draft-history.tsimport { Composer, Saved } from './composer';export class DraftHistory {  private stack: Saved[] = [];  constructor(private composer: Composer) {}  beforeEdit(at: number): void { this.stack.push(this.composer.save(at)); }  back(): void { const s = this.stack.pop(); if (s) this.composer.restore(s); }}
Pick one

Question 3 of 7

A team chat lets admins post from saved message templates, such as a lunch poll or a database-down alert. The files are below.

Which pattern are clone() and templates?

Code for this question
// templates.tsexport interface Template { title: string; clone(): Template; }export class Poll implements Template {  constructor(public title: string, public choices: string[], public closesInHours: number) {}  clone(): Poll { return new Poll(this.title, [...this.choices], this.closesInHours); }}export class IncidentAlert implements Template {  constructor(public title: string, public severity: 'sev1' | 'sev2', public pager: boolean) {}  clone(): IncidentAlert { return new IncidentAlert(this.title, this.severity, this.pager); }}export const templates = new Map<string, Template>([  ['lunch-poll', new Poll('Lunch?', ['pizza', 'sushi'], 2)],  ['db-down', new IncidentAlert('Database down', 'sev1', true)],]); // post.tsimport { templates, Template } from './templates';export function fromTemplate(name: string, title: string): Template {  const found = templates.get(name);  if (!found) throw new Error(`no template ${name}`);  const message = found.clone();  message.title = title;  return message;}
Pick one

Question 4 of 7

A team chat sends notifications. The files are below.

Which pattern is the split between Notice and Channel?

Code for this question
// channels.tsexport interface Channel { send(to: string, text: string): string; }export class PushChannel implements Channel {  send(to: string, text: string): string { return `push ${to}: ${text}`; }}export class EmailChannel implements Channel {  send(to: string, text: string): string { return `email ${to}: ${text}`; }} // notices.tsimport { Channel } from './channels';export abstract class Notice {  constructor(protected channel: Channel) {}  abstract deliver(to: string, about: string): string[];}export class MentionNotice extends Notice {  deliver(to: string, about: string): string[] { return [this.channel.send(to, `You were mentioned in ${about}`)]; }}export class DigestNotice extends Notice {  deliver(to: string, about: string): string[] { return about.split(',').map((t) => this.channel.send(to, `New in ${t}`)); }}
Pick one

Question 5 of 7

A team chat parses each message into a tree before sending it, and walk (given, below) visits every node. Node types: message, paragraph, quote and link have children; text and code have a value; mention has a userId; link also has an href. Fill in notifyList(authorId) in notify-list.mjs. It returns { visitor, result }: visitor is an object walk can take, and result() returns { users, hosts }, the user ids mentioned (first-seen order, each once, never authorId) and the host names of the links (first-seen order, each once, from new URL(href).hostname). Two lists made by two calls share nothing.

Write notifyList in full.

Code for this question
// walk.mjs (given)export function walk(node, visitor) {  visitor[node.type]?.(node);  for (const child of node.children ?? []) walk(child, visitor);} // notify-list.mjsexport function notifyList(authorId) {  // your code}

Question 6 of 7

A film service's watch-party screen has a player, a comments panel and a reactions bar. The files are below.

Which pattern is WatchParty?

Code for this question
// party-parts.tsexport class Player {  position = 0;  constructor(private onSeek: (to: number) => void) {}  seekTo(to: number): void { this.position = to; this.onSeek(to); }  pause(): void {}}export class CommentsPanel {  lines: string[] = [];  post(line: string): void { this.lines.push(line); }}export class ReactionsBar {  mutedFor = 0;  constructor(private onBurst: () => void) {}  burst(): void { this.onBurst(); }  mute(seconds: number): void { this.mutedFor = seconds; }} // watch-party.tsimport { Player, CommentsPanel, ReactionsBar } from './party-parts';export class WatchParty {  comments = new CommentsPanel();  player = new Player((to) => { this.comments.post(`Jumped to ${to}s`); this.reactions.mute(3); });  reactions = new ReactionsBar(() => { this.player.pause(); this.comments.post('Paused for the reactions'); });}
Pick one

Question 7 of 7

A box office's seat map has three kinds of place: a numbered seat, a box and a standing area. The files are below.

Which pattern are PlaceOp and pricing?

Code for this question
// places.tsexport interface PlaceOp<R> { seat(s: Seat): R; box(b: Box): R; standing(a: Standing): R; }export interface Place { run<R>(op: PlaceOp<R>): R; }export class Seat implements Place {  constructor(readonly row: string, readonly n: number, readonly stepFree: boolean) {}  run<R>(op: PlaceOp<R>): R { return op.seat(this); }}export class Box implements Place {  constructor(readonly name: string, readonly chairs: number) {}  run<R>(op: PlaceOp<R>): R { return op.box(this); }}export class Standing implements Place {  constructor(readonly zone: string, readonly capacity: number) {}  run<R>(op: PlaceOp<R>): R { return op.standing(this); }} // pricing.tsimport { PlaceOp } from './places';export const pricing: PlaceOp<number> = {  seat: (s) => (s.row < 'F' ? 60 : 45),  box: (b) => 90 * b.chairs,  standing: () => 30,}; // takings.tsimport { Place } from './places';import { pricing } from './pricing';export const takings = (map: Place[]): number => map.reduce((sum, p) => sum + p.run(pricing), 0);
Pick one

Go deeper

Pro tips
  1. When an interviewer names a pattern you have not used, restate its intent in one sentence and give the closest thing in your own stack. The curriculum row's pro tip. When: the row's typical question, "Which design patterns do you use most in your current codebase?" Verify: nothing to run; this is interview advice, and it keys nothing. Status: ADVICE.
  2. For "a pattern you have seen misused", name the problem it was meant for and show that the code did not have it. Step 10's costs are the usual stories: a flyweight with no memory pressure, a mediator holding every rule. When: the row's second typical question, "Name a pattern you have seen misused." Verify: nothing to run; this is interview advice, and it keys nothing. Status: ADVICE.
Source

All fetched 2026-10-17 by fetch-sources.mjs and saved as extracted text in sources/ with URL:, Read: and SHA256: header lines; nothing fetches at run time. Valid as of 2026-10-17.

  • refactoring.guru, The Catalog of Design Patterns (the row's second source): https://refactoring.guru/design-patterns/catalog (rg-catalog.txt). It lists 22 patterns under "Creational patterns", "Structural patterns" and "Behavioral patterns" and no Interpreter (step 1, step 9).
  • The catalogue's own pattern pages, linked from it, each at https://refactoring.guru/design-patterns/<name>: prototype, flyweight, bridge, mediator, memento, visitor (steps 3–8, 10 and every key), and facade, command, observer, strategy, iterator (the distractors' passages). The singleton, adapter and builder pages stay saved and key nothing since F29 ruling 15 replaced those three distractors with in-scope ones; the bridge, memento and visitor pages their passages come from were fetched again on 2026-10-18 and their bodies hash as saved. The interpreter page is missing (below). Teaching quotes: "A pattern isn’t just a recipe for structuring your code in a specific way. It can also communicate to other developers the problem the pattern solves." (bridge page; step 2); "The Flyweight pattern is merely an optimization." and "Over time a mediator can evolve into a God Object." and "Most dynamic programming languages, such as PHP, Python and JavaScript, can’t guarantee that the state within the memento stays untouched." (step 10).
  • https://refactoring.guru/design-patterns/interpreter answered HTTP 404 twice on 2026-10-17 (fetch-sources.mjs, retried once). Interpreter keys nothing here and is named in step 9 only.
  • The row's first source, Gamma, Helm, Johnson and Vlissides, Design Patterns (1994), is a book and keys nothing; step 1's "23" is the book's count, named, not cited.

Checked against the tools and docs as of 2026-10-17.