I did not author the game, and I can't remember the name of the word list, but there is even a video of how the game was built and the word list is part of it: https://www.youtube.com/watch?v=CPhP4NXJFRk
Your example, deduplicating the two functions into one, illustrates an interesting point, although I'd prefer still having the two specialized functions there:
def set_deadline(deadline):
if deadline <= datetime.now():
raise ValueError("Date must be in the future")
def set_task_deadline(task_deadline):
set_deadline(task_deadline)
def set_payment_deadline(payment_deadline):
set_deadline(payment_deadline)
set_task_deadline(datetime(2024, 3, 12))
set_payment_deadline(datetime(2024, 3, 18))
You lose absolutely nothing. If you later want to handle the two cases differently, most IDEs allow you to inline the set_deadline method in a single key stroke.
So the argument from the article...
> Applying DRY principles too rigidly leads to premature abstractions that make future changes more complex than necessary.
...does not apply to this example.
There clearly are kinds of DRY code that are less easy to reverse. Maybe we should strive for DRY code that can be easily transformed into WET (Write Everything Twice) code.
(Although I haven't worked with LISPs, macros seem to provide a means of abstraction that can be easily undone without risk: just macro-expand them)
In my experience, it can be much harder to transform WET code into DRY code because you need to resolve all those little inconsistencies between once-perfect copies.
I can only assume the Google example would be part of a script/cli program that is meant to crash with an error on a bad parameter or similar. Perhaps the point is to catch the exception for control flow?
My personal goal is to get things done in as few lines of code as possible, without cramming a bunch on one line. Instead of coming up with fancy names for things, I try to call it by the simplest name to describe what it's currently doing, which can be difficult and is subjective.
If we wanted to define a function which crashes like the example, I would probably write this:
def throw_past_datetime(dt):
if dt <= datetime.now():
raise ValueError("Date must be in the future")
If the point is not to crash/throw for control flow reasons, I'd write this in non-cli/script code instead of defining a function:
dt = datetime(2024, 5, 29)
if dt < datetine.now():
# Handle past date gracefully?
If it needs to do more in the future, I can change it then.
>You lose absolutely nothing. If you later want to handle the two cases differently, most IDEs allow you to inline the set_deadline method in a single key stroke.
Problem with unintentional coupling isn't that you can't undo it. It is that someday someone from some other team is going to change the method to add behaviour they need for their own use case that is different from your own and you won't even notice until there is a regression.
in this case (which shouldn't happen because it requires that you merged things that don't belong together - see accidental duplication), at least the one changing the method has all information on his hands and doesn't have to keep a potentially complex graph of copied code in his mind.