Idempotence and pedantic developers
I’ve been re-watching Jason Roberts’ Pluralsight course on Akka.NET recently. At one point he creates and handles a StopMovieMessage. If nothing is playing he logs an error if nothing is playing at the time.
In this case it’s a throwaway example just to show that something’s happening; however, if I lift it out of that context it reminds me of quite a common mindset or pattern. Pedantic developers tend to forget what they’re actually promising to do [*].
What’s Jason actually trying to do with a StopMovieMessage? Is it to actively stop a running movie, or is it to put the system into a state where no movie is running? There is a subtle difference, but the impact on users of your app or framework can be significant.
Developers often approach code having seen, probably developed, the implementation. In this case, they’d be aware that the system might or might not be playing a movie and be focused on that detail, making a request to stop a movie that’s not playing seem absurd, a clear error. From that viewpoint it’s logical to log an error to point out that you were asked to do something daft; it’s standard developer pedantry. Moreover, it might actually be useful while developing the app: if you’re writing the client code as well you might well be interested in cases where you’re unnecessarily asking for movies to be stopped.
However, an independent client might just not care. Worse, the code might throw an exception when the client doesn’t care – it just wants to put the system into a state where there is no movie playing. Now it has to handle an exception it doesn’t care about – it must differentiate between the case where a movie wasn’t playing and a case where the movie can’t be stopped for some important reason.
What’s the consequence? Well, generally it’s some complication and potentially loss of performance. A simple unnecessary error log can become critical if it appears a few thousand times in a log and makes it difficult to find real problems. If the call throws no exceptions but this then you’ve added exception handling and a no-op to the client code for no real reason. Even if the call throws other exceptions then the client code gets an expensive effective no-op in which you catch and parse the unrequired exception and throw it away. In truly awful cases, the method call might error uneccessarily with the side-effect of leaving the system in an unexpected state, requiring the client to tidy up.
It takes an effort of will to lift your focus out of the code you’re in and out to its real design goal; being very clear about what a client would want to achieve in using your code – and using the Single Responsibility Principle in its fullest form, defining exactly what a method does, is one way of making sure that we don’t add unnecessary complexity.
[*] note that I’m clearly not describing Jason as pedantic, partly because it’s never clear whether it’s meant as an insult or a compliment for a developer, and partly because we’ve never met :) “Pedantic developers” is probably tautological.