Skip to content

Substitute Algorithm

Some functions are hard to read not because they are long, but because the approach is convoluted — a hand-rolled loop where a lookup would do, a chain of conditionals that re-implements something the standard library already offers. Substitute Algorithm swaps the whole body for a simpler one that yields identical results. Unlike the smaller composing moves, this one replaces the strategy, not just the wording, so it leans hard on your tests to prove the two approaches agree.

The function works, but reading it you find yourself simulating it in your head to be sure it is correct. The logic feels heavier than the problem deserves: a manual search through a list when a set membership check would answer it, or a sequence of if branches that boil down to “is this value one of these few?”. When you can describe the function’s job in one plain sentence yet the code takes a paragraph, a clearer algorithm is probably available.

A function decides whether a country code is supported by looping and flipping a flag. The substitute uses a direct membership check against a fixed set — same answer, far less to read.

// Before
function isSupported(code: string): boolean {
const supported = ['US', 'CA', 'GB', 'DE'];
let found = false;
for (const c of supported) {
if (c === code) {
found = true;
}
}
return found;
}
// After
const SUPPORTED = new Set(['US', 'CA', 'GB', 'DE']);
function isSupported(code: string): boolean {
return SUPPORTED.has(code);
}
  1. Make sure the function you are about to replace is fully covered by tests, including its edge cases — empty input, no match, the boundary values. If coverage is thin, add tests first and confirm they pass against the existing code.
  2. Work out the replacement algorithm completely and write it, ideally as a separate function beside the original.
  3. Compare the two behaviours. For a function with a small, enumerable input space, run both over every input and assert they agree. Otherwise rely on your test suite plus a few targeted cases.
  4. Swap the old body for the new one.
  5. Run your tests. Every case must still pass.
  6. Remove the old algorithm and any helpers it needed once nothing references them.

Substitute Algorithm pays off when a clearer or standard approach exists that produces the same result — replacing a bespoke loop with a library call, or a sprawling conditional with a table lookup. The new version is easier to read and usually easier to extend.

The risk is higher than the smaller moves because you are changing the strategy wholesale, so subtle behaviours can differ — a different tie-breaking order, a changed handling of duplicates or empty input. Only attempt it with solid tests, and prefer to break a giant convoluted function into smaller pieces before substituting, so each replacement stays small and verifiable.

What does Substitute Algorithm replace?
Why does this refactoring rely especially heavily on tests?
What is a good preparatory step before substituting an algorithm?
How should you handle a giant, convoluted function you want to simplify?