The Dying Art of Pragmatism in Software Development
Wednesday, January 29, 2020
In order to be a Pragmatic Programmer, we're challenging you to think about what you're
doing while you're doing it. This isn't a one-time audit of current practices--it's an ongoing
critical appraisal of every decision you make, every day, and on every development. Never
run on auto-pilot. Constantly be thinking, critiquing your work in real time.
This is generally sound advice, but "critically appraising every decision you make" does – at its core
– fly a bit in the face of being pragmatic (where we're urged to make decisions based on what
makes sense practically). Practically speaking, not every decision warrants such critical scrutiny.
One debate that I think is really an example of well-intentioned pragmatism gone off the rails is
that of "boolean parameters are evil".
Martin Fowler has written about this,
as have many others.
The gist of the argument is exactly what it sounds like: using boolean parameters for functions
is a bad thing. Doing so can negatively impact readability and cohesion, can make the underlying
function messy, and can violate other design principals. As one pundit put it:
A Boolean Parameter effectively permits a method's caller to decide which execution path to take. This is a case of bad cohesion. You're creating a dependency between methods that is not really necessary, thus increasing coupling.
from https://github.com/troessner/reek/blob/master/docs/Boolean-Parameter.md
First off, the same argument could be made about any number of parameter types.
A function such as
MakeApiRequest(string url, bool useHttpPost) could just as well be
MakeApiRequest(string url, string getOrPost) or
MakeApiRequest(string url, HttpGetOrPost method) .
Each one of these functions is allowing the caller to
determine an execution path at some point.
A commonly-cited example of an "evil boolean" is a function which displays
a dialog box and takes a boolean parameter such as "showAnimated".
Some folks will argue that this is a problem and that there should instead be both a
ShowDialog and ShowDialogAnimated function, for example.
Someone will undoubtedly cry, "Single Responsibility Principle! SRP! That function should be doing one thing only!"
And that's where I feel that things start to get a bit silly.
Without knowing the underlying implementation that displays the dialog box, it's unfair (and unwise) to make
a blanket statement that using a boolean "showAnimated" parameter is the wrong design decision.
Nonetheless, to appease our critics, we take our ShowDialog method and split it up into two
methods. Once we've done that, we point out how much more "SRP" we are now – as well as how much
less DRY (don't repeat yourself) we are. In an effort to "do one thing" (and to avoid an evil
boolean), we duplicated some amount of code between the two methods. At this point, the
discussion could go any number of directions (or could just fall apart altogether...)
Now, don't get me wrong. If a boolean "doItTheOtherWay" parameter (here, showAnimated) causes the implementation to go
down two drastically different code paths – or, as in Fowler's example, causes a lot of interleaving
checks for if (doItTheOtherWay) – then sure, maybe a boolean parameter in this case isn't the best
approach and two separate methods would be preferable. To use one example, however, as a guideline or
mantra (boolean parameters are evil!) is simply not being pragmatic. Just because there are cases
where using a boolean parameter may not be the best option, this is no reason to declare boolean
parameters evil – or even a "code smell" for that matter.
Some will argue the point from the perspective of code readability. "I see the call to
ShowDialog(true) , but how am I supposed to know what 'true' means here? I don't want to have
to look at the implementation/docs/etc. to find out!" Well, my short answer to this is, "That's
your job."
Some will argue that a more-readable enum should be used instead: ShowDialog(DisplayMode.Animated) .
Sure, this is more apparent on the surface, but does it really matter here? Is introducing a
new enum simply to avoid using a boolean really necessary? Is it really practical?
So, we rub our magic lamp, a genie pops out, and we wish for no more evil boolean parameters. Poof! Wish
granted. We scroll down a few lines, however, and we find this:
DisplayAsciiBanner(80, 25, 2);
Now what? Declare int parameters evil? Where does the madness end?
With respect to readability, the two examples are really no different: in both cases, we need to check something
(Intellisense,
the function signature, docs, whatever) to determine what the parameters represent. "true" means nothing more
or nothing less here in ShowDialog than do 80, 25, and 2 in DisplayAsciiBanner.
Sure, something like this (if/when supported by the language) would be nicer, perhaps:
DisplayAsciiBanner(targetWidth: 80, targetHeight: 25, lineSpacing: 2);
Tools like ReSharper can
provide this insight automatically for you if not done explicitly in the code. But again, if
you're expecting to know what every line of code is doing without having to dig a bit, you may
need to re-evaluate your expectations.
Part of being pragmatic is keeping in mind that nothing is cut-and-dried, so trying to blindly
adhere to SRP, DRY, etc. because "that's the right way to do it" is basically the antithesis of
being pragmatic. Being pragmatic requires us to realize that everything is a balancing act:
Will adhering to SRP violate DRY? Will adhering to DRY violate SRP? Does it matter in a given
instance?
If you're a student or a hobbyist programmer and you go through these types of exercises strictly
for the theoretical insight, then more power to you, I say. As a professional programmer, however,
part of being pragmatic is asking yourself, "does it matter?"
In the time you spent thinking about whether to use a boolean parameter or two separate methods, then
checking opinions on Stack Overflow, then going down the rabbit hole with articles about evil boolean parameters,
you could have written one version of the code, refactored it 180 degrees, come to the realization
that it doesn't really matter in your case (both approaches work just fine and are equally easy to
read and maintain),
and been on to other more pressing items.
Being a truly pragmatic programmer is about more than simply critiquing our design decisions –
it's about knowing which decisions are worth spending the time and effort to critique.
|
||||