A method whose body is a switch or chain of `if` branches dispatching on a type code. Adding a new variant means finding every dispatcher in the codebase and editing each one; the conditional grows wider every time a variant lands.
A method whose body the agent must trace through a chain of branches to determine what runs. Each branch hides domain logic; verifying behavior requires loading every branch in scope on every edit.
Each variant lives in its own Strategy class implementing a shared interface. Dispatch happens through a single virtual call — the conditional vanishes from the call site.
Each variant lives in its own class; the agent can verify one strategy's behavior without loading the others. The dispatch site is a one-line call that needs no per-variant reasoning.
Before the refactoring
class Loan {capital() {if (this.expiry === null && this.maturity !== null) {// term loanreturn this.commitment * this.duration() * this.riskFactor();}if (this.expiry !== null && this.maturity === null) {if (this.unusedPercentage !== 1.0) {// revolverreturn this.commitment * this.unusedPercentage * this.duration() * this.riskFactor();}// advised linereturn this.outstandingRiskAmount() * this.duration()+ this.unusedRiskAmount() * this.duration() * this.unusedPercentage;}return 0;}}
After the refactoring
class Loan {constructor(strategy) {this.capitalStrategy = strategy;}capital() {return this.capitalStrategy.capital(this);}}class TermLoanStrategy {capital(loan) {return loan.commitment * loan.duration() * loan.riskFactor();}}class RevolverStrategy {capital(loan) {return loan.commitment * loan.unusedPercentage * loan.duration() * loan.riskFactor();}}class AdvisedLineStrategy {capital(loan) {return loan.outstandingRiskAmount() * loan.duration()+ loan.unusedRiskAmount() * loan.duration() * loan.unusedPercentage;}}
Type-code variants drive Shotgun Surgery: one new variant touches every dispatcher in scope. Cross-branch invariants are easy to miss because no single place owns the per-variant logic.
Every edit to one branch re-loads the entire conditional. Cross-branch invariants compound context cost; reasoning about which branch fires when requires holding the type-code rules in working memory across the full method body.
Strategy introduces a class hierarchy and a runtime indirection. For two stable variants that will never grow, a plain conditional is often clearer and cheaper than the ceremony.
Strategy fragments related logic across files, increasing context-load cost when reasoning about the system end-to-end. Vtable dispatch obscures static call-graph analysis — the agent has to enumerate strategy classes by hand.
Adding a new variant is a new class — the dispatching code never changes. Each variant's behavior is locally readable and locally testable; the Liskov contract is the only cross-variant thing to mind.
Each strategy is independently verifiable. Adding a variant is one new file, not a multi-file change. The dispatching site stays trivial regardless of variant count; per-variant tests pin exactly the behavior they own.
Premature Strategy where the variants are stable, few, and unlikely to grow — the class hierarchy adds ceremony that earns no flexibility. One-method strategies whose only purpose is to satisfy the pattern make the system harder to read, not easier.
Splitting a small set of one-line cases into one-method strategy classes forces the agent to load N files to reconstruct what one conditional once expressed in one file; context cost multiplies without a corresponding reduction in per-edit scope.