re: Is “Defensive Programming” actually healthy? VIEW POST

FULL DISCUSSION
 

I'm probably a minority, but I really hate YAGNI. It is based on the assumption that the cost of doing things when the need arises is the same as doing them now. In practice, this assumption tends to be false, because by the time you'll actually need to change that code, two things almost always accumulate:

  1. New code that depends on the old, pre-change way is written.
  2. You lose the mental state you had when writing the original code.

These two factors make is significantly harder to implement the feature when you need it. You are not trading X hours of work now for X hours of work later with a probability of not needing it - you are trading X hours of work now for αX hours of work later, and suddenly it's no longer a matter of best practice dogmas - it's a matter of risk management and the estimated probability of not needing it matters.

Even if after considering everything you decide not to do it now, you should at least put some minimal effort to make it easier to do in the future - like creating stubs and writing more comments explaining things you that may be hard to recall later.

 

Good point. I also try to call out YAGNI whenever possible. But do you think that avoiding fallthrough in a switch case (as shown in the article) is YAGNI? I’m just curious.

 

YAGNI is flexible enough to support both. You can say it's YAGNI because supporting more than the current 3 states for a traffic light is not a feature you currently need, or you can say it isn't YAGNI because ensuring the code does not break when you change it is a feature you need.

If you treat YAGNI as a holy dogma then it make sense - as with all sacred scriptures - to force the definitions to match your opinion. I prefer to treat YAGNI - or any other best practice - not as a rule that cannot be broken (but can be twisted) but as a rule of thumb. Not "you shall not do this!" followed by a thunder noise but "you should take this into consideration.

So, in this case, YAGNI applies - you should consider the probability that you won't need to support more options. But that's just one factor you need to consider - you should also consider the probability you will need it (1 - ρ), the cost of doing it now, the cost of doing it later, and the potential bugs of both cases. And with everything considered - it's pretty clear that avoiding the fallthrough is simply not worth it.

Thank you for responding. :) I’m a bit confused by this point though:

it's pretty clear that avoiding the fallthrough is simply not worth it.

Because it takes about 15 seconds to add any else/default case that throws an error. And if you have an assertUnreachable function around, then it takes the same time to get compiler time feedback.

I totally 100% respect your thoughts on measuring the cost— after all, modern software development and Agile is all about trade offs. But if you just make it a rule of thumb to never fall through... you’ve avoided a whole class of bugs and it only took you 15 seconds per switch statement.

As for the use of the word YAGNI, another commenter provided this wonderful quote that has helped clarify my thoughts. Martin Fowler says:

“Yagni only applies to capabilities built into the software to support a presumptive feature, it does not apply to effort to make the software easier to modify.”

And after using the never pattern (described in my article) for a few months now, I can tell you that it makes refactoring so much easier. And to your point, maybe the refactoring is never required, but I do feel a lot happier in the present when I take the time to pay it forward to future me.

Sorry, that was probably a miscommunication. Since the regular meaning of "fallthrough" (missing break; statement in a switch clasue that causes execution to fall through from one case to another) does not apply here, I interpreted "avoiding fallthrough in a switch case (as shown in the article) is YAGNI" as the original code - the one that not modifying it "respects YAGNI".

Of course the option that does not potentially cause a lethal accidents when new options are added is preferable...

But if you just make it a rule of thumb to never fall through...

A "rule of thumb" does not mean "never do this" or "always do that". What it means is "always consider this". You still need to apply your own judgment.

Martin Fowler says: ...

Best practices are treated too much like holy scriptures. A set of rules, set in stone, that everyone can quote, and whether or not they know the origin of a rule - the assume that it came from God and must never be broken. But for a law to always fit reality it has to be very elaborated, and these best practices usually try to be short and catchy proverbs. So wise sages (like Martin Fowler here) add more interpretations and clauses to make it fit real life cases.

I really disagree with this approach. Developers like Martin Fowler simply apply their own judgment to the rule for everyone to use, but I think every developer should be capable of thinking for themselves and using their own judgment. You don't need to find some sage to quote to support your judgment - you can provide your own reasoning. Even if you don't have your biography and achievements listed in Wikipedia.

Of course, if your favorite sage published an article or wrote a blog post with well-built arguments there is no shame in linking it. The point is that you should rely on the logic of the arguments - be them your own or from external sources - and not the holy wisdom of the arguer.

Yup absolutely. I only quoted him because j thought he expressed a nice sentiment succinctly and I think it’s important to cite people for their contributions. As for my own thoughts: I have yet to find a good, safe reason to have a default case that handles more than one state (I thought that was called the fallthrough case but my bad). I think (without anyone else telling me) that it’s better to throw an error (or better yet, create a compiler error like I show in the article) when a not-yet-discovered case is found in the default.

I was hoping that someone would provide a reason to avoid the “rule of thumb.” I like discovering when ideas are not absolutes. The fun is in the gray area. But until someone presents a compelling reason for a non-never default case, I’ll continue to make it a correction on code reviews that are submitted to me. Defense it is.

There is one case I can think of where you want a default clause that does not throw an error - handling keycodes:

switch (keyboardEvent.keyCode) {
    case KeyCode.LEFT:
        return goLeft();
    case KeyCode.RIGHT:
        return goRight();
    case KeyCode.Up:
        return goUp();
    case KeyCode.DOWN:
        return goDown();
    default:
        return doNothing();
}

(in no particular language)

Adding ~100 more case clauses for all the other key codes is too much, and you wouldn't want this to fail compilation just because someone updated KeyCodes to support some more keys.

Whadya know, an exception to the rule. Bravo! 👏

But yea, I think exhaustively checking every case in the KeyCode enum would be a waste of time and would be way too verbose. I gotta be honest, I wasn’t expecting someone to come up with something that made me think it was wise to avoid the never assertion, but you did. :) I guess that’s he beautify of seeking feedback.

That's why my best practice is to never blindly follow best practices to the letter and always apply your own judgment.

code of conduct - report abuse