Refactoring: Express Selections as Tables
We now turn from principle to practice. The first pattern addresses one of the most common causes of long methods: selection logic buried in conditionals.
A surprising amount of code is just data pretending to be logic. It’s wearing a fake mustache.
Take a switch statement where each branch selects a value and returns it:
switch (creature) {
case DRAGON:
return new SnackRecommendation("Charcoal-grilled marshmallows");
case VAMPIRE:
return new SnackRecommendation("Tomato juice on ice");
case WIZARD:
return new SnackRecommendation("Mystic instant noodles");
default:
return new SnackRecommendation("Chef special surprise");
}
When selection logic is encoded as branching, we force the reader to simulate the program to discover a simple fact: there is a direct mapping from input X to value Y.
A switch with a few case labels might not be the worst idea, but I’m confident that we’ve all seen switch-cases stretching over tens and hundreds of lines of code. Each time an agent wants to add a new case, it has to modify that function.
A better solution is to state the relationship between input and result value directly. A table does that:
private static final SnackRecommendation RECOMMENDATION_FOR_MYSTERIOUS_GUEST =
new SnackRecommendation("Chef special surprise");
private static final Map<Creature, SnackRecommendation> RECOMMENDATIONS_BY_CREATURE = Map.of(
Creature.DRAGON, new SnackRecommendation("Charcoal-grilled marshmallows"),
Creature.VAMPIRE, new SnackRecommendation("Tomato juice on ice"),
Creature.WIZARD, new SnackRecommendation("Mystic instant noodles")
);
public SnackRecommendation snackFor(Creature aHungryOne) {
return RECOMMENDATIONS_BY_CREATURE.getOrDefault(
aHungryOne,
RECOMMENDATION_FOR_MYSTERIOUS_GUEST
);
}
This is not a Java-specific trick. In Python, the same idea is usually expressed with a dictionary. In C#, you’d typically use a Dictionary<TKey, TValue>. The syntax differs, but the pattern is the same: move the mapping out of control flow and into a data structure that states the relationship directly.
Refactoring to a table makes the design intent explicit: there is a fixed set of domain values, and each one maps to a recommendation. Better, any changes to the behavior of the code are now purely declarative. You modify the table, but don’t have to change any logic. That’s about as safe as it gets.
This refactoring has several benefits:
Separates selection data from selection mechanics.
Removes repetitive branching noise that adds no new meaning.
Makes missing cases and defaults easier to reason about.
Gives both humans and agents a single, canonical place to inspect and modify.
Reduces the amount of code an AI must read before making a safe change.
There is also a more subtle design gain here: once the selection becomes a table, it becomes easier to ask the right domain questions. Should this really be a fallback? Or should the default be an exception as a missing entry would indicate an internal bug? Those questions are much harder to see when the logic is buried in a switch.
When this refactoring applies
Use a table when branches are doing little more than selecting a value.
Typical signs:
Each branch returns a constant or near-constant value.
The logic is keyed by a domain concept such as status, type, code, or state.
There is little or no branch-specific algorithmic behavior.
In contrast, do not force everything into a table. If each branch contains meaningful behavior, side effects, or a non-trivial algorithm, then you probably have a behavioral variation problem rather than a selection problem. In that case, reach for polymorphism, composition, or dedicated strategy objects. (We’ll cover these patterns too later).
A blunt but useful rule is:
If your
switchmostly chooses data, use a table. If it mostly performs behavior, use objects or functions.
Why this matters more in the age of AI
Humans are good at glossing over repetitive code. We see a switch, we skim, and we tell ourselves we got the idea. Often we did. Sometimes we did not.
Agents must process the structure more mechanically and literally. A long conditional construct increases the amount of code they need to ingest before they can infer the domain model. That increases token cost and the chance of error. The code becomes mechanically readable but semantically vague.
A table improves that situation because it is closer to the underlying purpose of the program. Instead of reading branches and reconstructing a mapping, the agent sees the mapping directly. This is one of the recurring themes in AI-readable code: refactor toward explicit structure. Make the code say what it is.

