Software Design Reference Core Designer · Seeing Structural Problems

Lesson 0004 · Core Designer · Module 1

Recognizing When Strategy Is Emerging

Patterns are not applied — they are noticed. The Strategy pattern is already in your code the moment one behavior branches on a type. This lesson trains the noticing.

Mission tie-in: "recognize when patterns emerge naturally" is a core Designer skill; it's also the difference between AI that copies patterns and code that grows them from evidence.

Knowledge: the shape that already exists

The Strategy pattern's shape: a varying behavior extracted behind an interface, with implementations swapped at runtime without touching the caller. Before it is a pattern, it is a smell — an if/elif chain selecting behavior by a type field:

def discount(customer: Customer, total: Money) -> Money:
    if customer.tier == "gold":
        return total * Decimal("0.90")
    elif customer.tier == "silver":
        return total * Decimal("0.95")
    else:
        return total

Ask: what will change here? The set of tiers — new tiers appear, discounts change, and every edit lands in this one function. That is a variation point with a growing list of branches. The Strategy is emerging: the chain already plays the role of a strategy selector.

Making it explicit:

class DiscountStrategy(Protocol):
    def apply(self, total: Money) -> Money: ...

class GoldDiscount:
    def apply(self, total: Money) -> Money:
        return total * Decimal("0.90")

class SilverDiscount:
    def apply(self, total: Money) -> Money:
        return total * Decimal("0.95")

class NoDiscount:
    def apply(self, total: Money) -> Money:
        return total

def discount(strategy: DiscountStrategy, total: Money) -> Money:
    return strategy.apply(total)

Nothing about behavior changed — the chain was already a strategy; now it is a strategy with names and seams. New tiers become new classes instead of new branches.

The discipline: extract only when the variation is real and growing. One branch is a choice; two branches are a choice; the third repetition is when the rule of three starts whispering "strategy."
Field notes · what it looks like in real code
You see thisWhat it costsThe move
if kind == …: elif kind == …Three or more arms: every new variant edits one shared function.Extract one strategy per arm; the caller stops branching.
RATES = {"gold": 0.90, …}Data-only variation — no behaviour differs, so no strategy is emerging.Keep the lookup table; it is already the lighter answer.
two branches, unchanged for a yearExtracting now buys machinery for a variant that may never arrive.Keep the branches; write the variation point in a note.
the same tier chain in three filesThe variation point already leaked — three copies, one shape.Extract now: the third occurrence is the evidence.

Skill: see the strategy inside the code

Which signal says a Strategy is emerging?

Two branches exist today, with no sign of a third. The right move is:

After extracting the strategy, a new tier arrives. You:

Practice on your own code

Find an if/elif chain (or match) in your codebase that selects behavior by a field or string. Ask: is this a real variation point? Then decide, with evidence: extract now, or note it and keep watching.

Reveal: a worked judgment

A notification module branches on channel == "email" | "sms" | "push". Three channels exist and a fourth is planned. The branch is a variation point with growing list — extract NotificationChannel as a protocol with one send(). The caller selects by lookup, not by chain.

Your win

You now read code looking for variation points, not pattern names. When a chain selects behavior by type, you can name what's emerging — and, just as important, refuse to extract what isn't.

Read and watch deeper

Show your agent-teacher a branching snippet and compare judgments on whether a strategy is truly emerging.