Strategy Pattern vs If/Elif Ladders: When the Abstraction Pays For Itself


A refactor that made the code prettier and the on-call rotation worse

Somewhere in a billing service I inherited, there was a function that calculated fees differently depending on payment provider — Stripe, PayPal, a couple of regional processors. It was an if/elif ladder, about nine branches deep, and it offended me the way if/elif ladders offend most people who’ve read a design patterns book. I refactored it into a Strategy pattern: one class per provider, a common interface, a registry to look them up. It was better code, by every metric that shows up in a linter. It made the next incident worse.

What went wrong

A new payment provider’s fee calculation had an edge case around currency rounding. Under the old if/elif version, you’d find the whole rounding logic for every provider in one function, in one file, and you could read all nine branches side by side to see how each one handled the edge case, then copy the pattern that worked. Under the Strategy version, the same information was spread across nine files, each implementing a common interface but with no forced proximity — so understanding “how does everyone else handle currency rounding” meant opening nine files and holding the pattern in your head across all of them, instead of scrolling one function.

The abstraction was correct in the “each provider is a variant of the same operation” sense. It was wrong in the “how do people actually debug fee calculation” sense — which was almost always by comparing providers against each other, not by working within one provider’s isolated logic.

The actual heuristic

The Strategy pattern (or any polymorphism-over-conditionals refactor) pays for itself when:

It doesn’t pay for itself when:

What I’d do differently

I’d have kept the if/elif ladder, and split it only if we’d actually started onboarding new payment providers at a rate that made the file unwieldy — which, a year later, we hadn’t. The lesson wasn’t “if/elif is fine, patterns are overrated.” It was that I refactored based on what the code looked like in isolation, not based on how the team actually used it during an incident. That second question is the one that should decide it.