Design Patterns skill for Codex

Use a pattern only when change demands it. Codex names the concrete change pressure before reaching for a pattern, then picks the lightest form the language offers, often a typed map of functions instead of a class hierarchy. Refactors start with characterization tests and land as separate behaviour-preserving commits, and over-engineered interfaces with a single implementation get collapsed rather than defended.

  • Architecture
  • $design-patterns
  • 157 lines
  • 2 files
  • ~1,750 tokens
  • MIT
  • Updated

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- design-patterns

Installs to ~/.agents/skills/design-patterns/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $design-patterns, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Chooses and applies Gang of Four and related design patterns (strategy, factory, adapter, decorator, observer, command, state, builder, template method, facade) only where a concrete change pressure justifies them, and refactors toward or away from patterns in small, test-backed steps. Use when a switch on type keeps growing, a class mixes several variants, code needs a seam for a new provider, someone asks which pattern fits, or an over-engineered abstraction should be collapsed.

Use it when

  • A switch or if chain on a type keeps growing or is duplicated
  • A new provider, format or variant needs a clean seam
  • Someone asks which design pattern fits a problem
  • An abstraction has one implementation and only adds indirection

Use something else when

How to check the result

Characterization tests pass unchanged, the type checker rejects a deliberately missing variant, and adding the next variant touches a single file.

The mistake it prevents

Adding interfaces, factories and registries for a variant that never arrives, which costs more to read than the duplication it replaced.

~/.agents/skills/design-patterns/SKILL.md

--- name: design-patterns description: Chooses and applies Gang of Four and related design patterns (strategy, factory, adapter, decorator, observer, command, state, builder, template… license: MIT ---

Done well means each pattern in the code answers a change that has happened or is scheduled (a second provider, a third variant, a new output format), the pattern uses the lightest form the language allows (often a function or a map, not a class hierarchy), and the refactor lands as a series of green commits that do not change behaviour.

Before changing anything

  1. Read AGENTS.md and nearby code. Match the codebase’s idioms: a functional TypeScript codebase should not suddenly grow abstract base classes.
  2. Name the change pressure in one sentence: “We are adding a third payment provider”, “Every new report format edits the same 400-line switch”. No pressure, no pattern.
  3. Check test coverage of the code you will reshape. If it is thin, add characterization tests first (record current outputs for representative inputs).

Smell to pattern

Smell in the codePatternLightest form
switch/if chain on a type or mode, repeated in several placesStrategy (or polymorphism)Map from key to function
Calling code must know which concrete class to buildFactory functionOne function returning the interface
Third-party API shape leaks into your domainAdapterThin wrapper class/module owning the translation
Cross-cutting behaviour (caching, retries, metrics) wrapped around callsDecoratorHigher-order function or middleware
Many modules need to react when something happensObserver / domain eventsTyped event emitter or in-process bus
Operations must be queued, logged, undone or retriedCommandPlain data object plus a handler
Behaviour depends on a lifecycle status with guarded transitionsStateTransition table; state classes only when behaviour differs a lot
Constructor with many optional parametersBuilderNamed/keyword arguments or an options object first
Same algorithm skeleton, varying stepsTemplate methodFunction taking the varying steps as parameters
Callers juggle five subsystem objects for one taskFacadeModule exposing the one task

Prefer composition to inheritance. Template method and deep hierarchies make sense mainly in frameworks that require subclassing.

When not to use a pattern

  • One implementation, no second one on the roadmap: write the concrete code. An interface can be extracted in minutes when the second case arrives.
  • The language already does it: iterators, first-class functions, decorators in Python, Comparable/Comparator, dependency injection containers.
  • The pattern would need more lines than the duplication it removes.
  • Singleton as global state. Use a single instance created at the composition root and passed in; the GoF Singleton makes testing and concurrency harder.

Refactoring toward a pattern

Example: replacing a type switch with a strategy registry.

Before:

function shippingCost(order: Order): number {
  switch (order.carrier) {
    case "ups":   return 5 + order.weightKg * 1.2;
    case "fedex": return 7 + order.weightKg * 0.9;
    case "dhl":   return order.international ? 25 : 9;
    default: throw new Error(`unknown carrier ${order.carrier}`);
  }
}

After, in small steps:

type CostRule = (order: Order) => number;

const rules = {
  ups:   (o) => 5 + o.weightKg * 1.2,
  fedex: (o) => 7 + o.weightKg * 0.9,
  dhl:   (o) => (o.international ? 25 : 9),
} satisfies Record<Carrier, CostRule>;

export function shippingCost(order: Order): number {
  return rules[order.carrier](order);
}

satisfies Record<Carrier, CostRule> makes the compiler fail when a new carrier is added to the union without a rule, which the old default branch only caught at runtime.

Steps:

  1. Characterization tests: one case per branch plus the default/error case. Run green.
  2. Extract each branch body into a named function in place. Run tests.
  3. Introduce the registry and route through it. Run tests.
  4. Delete the switch. Run tests, type check, lint.
  5. If other switches on the same key exist, route them through the same registry or give the strategy object several methods. Duplicated switches on one key are the strongest signal for this refactor.

Class-based form, when strategies carry state or several methods:

class Exporter(Protocol):
    content_type: str
    def render(self, report: Report) -> bytes: ...

EXPORTERS: dict[str, Exporter] = {"csv": CsvExporter(), "pdf": PdfExporter(fonts)}

Refactoring away from a pattern

Collapse an abstraction when it has one implementation, its interface mirrors that implementation method for method, and no test uses a fake of it. Inline the interface, merge factory and product, delete the indirection. This is as legitimate as adding one.

Pattern notes that matter in practice

  • Adapter: keep the adapter responsible for translating errors and units too (vendor exceptions to domain errors, cents to Money). Leaking vendor exceptions defeats it.
  • Decorator: preserve the wrapped interface exactly, including cancellation and async behaviour. A logging decorator that swallows exceptions changes semantics.
  • Observer: decide synchronous or asynchronous delivery explicitly; synchronous handlers run inside the publisher’s transaction and their exceptions propagate to it. Unsubscribe or use weak references to avoid leaks in long-lived processes.
  • Command: give commands an id if they may be retried; handlers must be idempotent.
  • State: write the transition table first (current state, event, guard, next state). Illegal transitions should raise, not silently no-op.
  • Builder: validate in build(), not in setters, so partially built objects never escape.

Gotchas

  • Introducing a pattern and changing behaviour in the same commit makes review and bisecting impossible. Keep structure and behaviour changes separate.
  • Pattern names in class names (OrderStrategyFactoryImpl) are noise; name by role (ShippingRates, ExportFormat).
  • Registries keyed by strings need a single source of truth for keys (enum or union type) or typos fail at runtime.
  • Generic “plugin” architectures for one plugin are speculative generality.

Verification

  • Characterization tests written before the change pass unchanged after it.
  • Type check and lint pass; exhaustiveness (satisfies, sealed classes, match with assert_never) catches a deliberately removed variant. Try removing one and confirm the build fails, then restore it.
  • Adding the next variant (the change pressure you named) now touches one place. Show this in the summary by listing the files a new variant would edit.

Output template

## Refactor: <area> to <pattern or "collapse">
**Change pressure:** <the concrete reason>
**Before:** <shape, e.g. 3 switches on carrier across 2 files>
**After:** <shape, e.g. registry in shipping/rules.ts, exhaustive over Carrier>
**Adding a new variant now touches:** <files>
**Commits:** 1) characterization tests 2) extract functions 3) registry 4) delete switch
**Tests:** <command, result>

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "Design Patterns"
  short_description: "Use a pattern only when change demands it"
  default_prompt: "$design-patterns This switch on payment provider is copied in four files, refactor it properly"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc
New

Introducing Codex Skills Pro

79 professional skills that take Codex through complete engagements, each with its own scripts, references and a report that verifies the result.

All-AccessOne-time payment, no subscription
$49
Get All-AccessOr browse individual skills from $2.99 →

Secure checkout by Creem