A couple of examples:
ACTIONS = {
“swim”: Swimmer,
“skate”: Skater,
}
# …pages of code…
def f(action, *args):
…
mod = ACTIONS[action]
mod(*args).do()
At face value, this pattern doesn’t do anything particularly wrong but anyone who arrives at f on their code safari won’t see any symbols for either Swimmer or Skater. It breaks their flow the same way switching languages might do. Sure, ACTIONS is only a hop away, but that’s a level of indirection that could have been avoided. Even worse, what if the class name was derived from a string?The fact that the action implementations are chosen at runtime also makes life a bit awkward but the primary issue is not being able to immediately see the action classes and jump to their implementations.
Another one is calling shell scripts. We all have projects, I’m sure, where we have to shell out to some common legacy tool:
def f():
run(“legacy.sh”)
def g():
run(“legacy.sh”)
Wrapping the script in a native function does wonders for making it easier to find, especially for the poor, sacred soul who deprecates it from the codebase one day: def f():
run_legacy()
def g():
run_legacy()
# …pages of code…
def run_legacy():
run(“legacy.sh”)
Any unfriendly pattern that breaks tag-hopping / IDE-integration is yet another small chafing of your colleagues‘ productivity. Think twice before you introduce that YAML file which defines behavior, instead of implementing it natively — your friends (and their jump-to-definition muscle memories) will thank you.