skills/code-quality/design-pattern-suggestor/SKILL.md
Recognizes structural situations that match known design patterns and recommends whether to apply them — or explains why the pattern doesn't fit. Use when the user has a structural problem and is considering a pattern, when reviewing a design that uses a pattern questionably, or when the user asks which pattern fits their situation.
npx skillsauth add santosomar/general-secure-coding-agent-skills design-pattern-suggestorInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
A pattern is a named solution to a recurring structural problem. The value is the vocabulary, not the code. The risk is applying a pattern because it's familiar, not because the problem it solves is present.
Start from the symptom. The pattern is the answer; the problem is the question.
| Symptom | Pattern | Or maybe just… |
| ----------------------------------------------------------------- | ----------------- | ----------------------------------------- |
| Switch on type code, same switch in 3+ places | Strategy / State | …one switch if it's only in one place |
| Constructor has 8 parameters, 5 are optional | Builder | …keyword arguments with defaults |
| Need exactly one of a thing, globally | Singleton | …a module-level variable (in Python/JS) |
| Class hierarchy × orthogonal dimension = class explosion | Decorator / Bridge| …composition with a field |
| Creating objects, but which concrete type depends on runtime data | Factory | …a dict {key: constructor} — often enough |
| Many objects need to know when one object changes | Observer | …a callback list — that IS observer |
| Different interface than what you have, can't change either side | Adapter | Usually correct, rarely over-applied |
| Need undo, or need to queue/log/serialize operations | Command | Usually correct |
| Traversing a composite structure uniformly | Visitor / Iterator| …if the structure rarely changes, just recurse |
Most pattern misuse is applying GoF ceremony where the language already gives you the mechanism for free:
strategies = {"a": fn_a, "b": fn_b} is the pattern.import config — done.addEventListener, signals, Rx — the pattern is built into the runtime.yield / generators. You don't write hasNext()/next().If the "or maybe just" fits, use it. The pattern is the fallback for languages that lack the mechanism.
Sometimes the finding is "you're using a pattern wrong":
| Anti-pattern | Symptom |
| --------------------------------------- | --------------------------------------------------------------- |
| Singleton-as-global | Singleton exists to avoid passing a dependency — hidden coupling, untestable |
| Factory-for-one | UserFactory.create() returns new User() — the factory adds nothing |
| Observer soup | 10 observers, firing order matters, some observers trigger other observers — you've built an implicit state machine |
| Premature Strategy | One concrete strategy. The interface exists "in case we need another." → speculative generality |
| Visitor on a stable hierarchy | The hierarchy never changes; visitor's indirection buys nothing |
Symptom: "Every time we add a payment method we have to edit process_payment() and validate_payment() and refund_payment() — three switches on method_type."
Match: Same switch, 3 places, on a type code → Strategy.
Check the "or maybe just": What language? Java. No first-class functions pre-lambdas (or the team doesn't use them). Strategy is the idiomatic fit.
Recommendation:
Three switches on
method_type, growing with every new payment method → Strategy pattern.interface PaymentMethod { void process(Payment p); ValidationResult validate(Payment p); void refund(Payment p); } class CreditCard implements PaymentMethod { ... } class PayPal implements PaymentMethod { ... }New payment method → one new class, zero edits to existing switches.
Counter-check: Do
process/validate/refundalways change together when a method is added? Ifvalidateis actually the same for 4 of 5 methods, Strategy will force you to duplicate it. Consider: Strategy forprocess/refund, a singlevalidatewith a small type check.
## Symptom
<the structural problem — what hurts>
## Pattern fit
<pattern> — <why it matches this specific symptom>
## Or maybe just…
<simpler alternative if one exists — and when to prefer it>
## Sketch
<minimal code showing the pattern applied to THIS case, not a textbook example>
## When NOT to
<conditions under which this recommendation would be wrong>
development
Extracts human-readable pseudocode from a verified formal artifact (Dafny, Lean, TLA+) while preserving the verified properties as annotations, so the proof-carrying logic can be reimplemented in a production language. Use when porting verified code to an unverified target, when documenting what a formal spec actually does, or when handing a verified algorithm to an implementer.
development
Translates natural-language or pseudocode descriptions of concurrent and distributed systems into TLA+ specifications ready for the TLC model checker. Identifies state variables, actions, type invariants, safety properties, and liveness properties from the description. Use when formalizing a protocol, when the user describes a distributed algorithm to verify, when designing a consensus or locking scheme, or when starting formal verification of a concurrent system.
testing
Reduces a TLA+ model so TLC can actually check it — shrinks constants, adds state constraints, abstracts data, or applies symmetry — when the state space is too large to enumerate. Use when TLC runs out of memory, when checking takes hours, or when a spec works at N=2 and you need confidence at larger scale.
development
TLA+-specific instance of model-guided repair — reads a TLC error trace, identifies the enabling condition that should have been false, strengthens the corresponding action, and maps the fix to source code. Use when TLC reports an invariant violation or deadlock and you have the code-to-TLA+ mapping from extraction.