How properly using the Strategy Pattern affected my SaaS

By Fuat Can Koseoglu,

Jokes aside—keep functions pure; let the orchestrator handle sequencing. What went wrong? • PR smell: PhysicalDamageStrategy.Calculate() asks Magic what it would do and queries Poison for stack count. • Escalation: Week 1 clean strategies → Week 2 “Physical should know penetration” → Month 2 circular DI + strategies calling strategies. Result: Strategy morphed into a hidden orchestrator—a conditional web. Correct roles • Strategy: single algorithm; pure, stateless, zero-alloc in hot paths. • Context: data any strategy needs (e.g., IgnoresArmor, PoisonStacks, Armor, BaseDamage); readonly during calc; flags precomputed upstream. • Orchestrator: selects strategies, sets order/precedence (e.g., penetration → armor → DoT), aggregates results. Strategies never call peers. Why teams slide • Performance paranoia • Convenience creep • Feature pressure (local wins, global debt) Real production costs • Cycles: peer deps create DI loops and fragile builds. • Tests: N mocks to test 1 class → slow, flaky suites. • Hot path: extra branches/type checks hurt P95 per-hit / frame time. • Allocs: LINQ/boxing/closures in inner loops → GC spikes. • Order bugs: hidden precedence in “strategies” breaks determinism/rollback. PR Litmus (merge blockers) • No peer DI: strategies never depend on strategies/services (stateless helpers OK). • Pure & deterministic: same Context ⇒ same result; no I/O/logging/time/random/global reads; no Context mutation. • Data, not types: no is/as/type switches; branch on Context enums/ids/indices. • Zero-alloc hot path: per-call allocations = 0 (CI profiler). • Orchestrator owns interactions: no cross-strategy calls or precedence in strategies; resolve combos via tables/visitor. • Tests required: unit tests for strategies (determinism/bounds) + orchestrator integration tests (order/precedence). • Perf budget (forward-compatible): block merge if P95 per-hit > X µs or frame time +Y ms (tune per platform/build). Key insight: If a “strategy” asks another what to do, it is the orchestrator—move that logic where it belongs.