Hardcoded rules โ plugin architecture
Every time a new type showed up, we forked the existing code and modified it. Changing one policy meant touching the entire execution flow.
Split into three layers โ state, decision-making, execution โ and adding a new type became a matter of composing them. There are now 10+ types running on the same framework.
- State layer: an abstract interface defines the shared contract; type-specific differences live in composition, not inheritance. Nine variants in production
- Planning layer: an ABC that owns the decision lifecycle. Seven concrete planners (~3,230 LOC) inherit from it
- Execution layer: template-method pattern. Type-specific executors register dynamically at runtime
- Didn't try to generalize upfront. Built five types concretely first, and only after the shared lifecycle was obvious did I replace ~1,442 LOC of duplication with ~1,404 LOC of shared infrastructure